//::///////////////////////////////////////////////
//:: Combat Dummy
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*

  usage:

  assign this script to the OnDamaged event of a placeable
  make placeable non-static & usable
  assign 10.000 hp to the placeable
  set hardness and all saves to 0

*/
//:://////////////////////////////////////////////
//:: Created By: Knat
//:: Modified by styzygy
//:://////////////////////////////////////////////

// -------------------------------------------------------------------
// --------------- COMBAT DUMMY START --------------------------------
// -------------------------------------------------------------------
/*

 combat dummy

 does not need a creature object to simulate and track combat status.

 player needs to deplete a set amount of HP, depending on their level, to complete
 a training session.

 rewards XP one time during a specific timespan (default 1 hour realtime) ONLY
 if they complete a training session. (no xp if they abort/cancel a session)
 players will be able to re-train asap, but will not get any XP until said timer runs out.
 it's not possible to "farm" xp, it's just a small spice up...

 you can change those values easily.

 combat dummy records some basic statistics like number of hits and total damage
 done. It displays them during combat and a final statistic after the session is complete.

 adds FX based on player damage

 accounts for multiplayer.. only one active training session per dummy. other PCs attacking the
 same dummy get ignored (in theory at least, never tested so far)

*/

int   PLC_DUMMY_HP_MODIFIER = 10;      // dummy-hp = modifier + (pc-level * modifier)
float PLC_DUMMY_XP_MODIFIER = 0.0;     // adjust this to control XP output (xp = dummy-hp * modifier)
//float PLC_DUMMY_XP_TIMER    = 3600.0;  // XP timer in seconds. Player won't get XP again until this timer runs out

void DummyRespawn(string sResRef, location lLoc)
{
  CreateObject(OBJECT_TYPE_PLACEABLE, sResRef, lLoc);
}

void DummyCheckSession(object oDummy = OBJECT_SELF)
{
  object oUser = GetLocalObject(oDummy,"PLC_INTERACT_USER");
  if(GetIsObjectValid(oUser))
  {
    if(GetIsInCombat(oUser)) // still in combat, restart timer
      DelayCommand(5.0,DummyCheckSession(oDummy));
    else
    {
      // not in combat, abort session
      FloatingTextStringOnCreature("Session Aborted...",oUser);

      // delete & re-create dummy
      string sResRef = GetResRef(oDummy); location lLoc = GetLocation(oDummy);
      AssignCommand(GetModule(),DelayCommand(0.1,DummyRespawn(sResRef,lLoc)));;
      DestroyObject(oDummy);
    }
  }
}

void DummyMain(object oPC, object oDummy)
{

  // initial user interaction, starts training session
  if( !GetLocalInt(oDummy,"PLC_INTERACT_INUSE") )
  {
    //DelayCommand(5.0,CombatDummyReport(oPC, oDummy));
    // set target PC
    SetLocalObject(oDummy,"PLC_INTERACT_USER",oPC);
    // set InUse flag
    SetLocalInt(oDummy,"PLC_INTERACT_INUSE",TRUE);
    // set strength of dummy based on player level
    SetLocalInt(oDummy,"PLC_INTERACT_SESSIONHP", (GetHitDice(oPC) * PLC_DUMMY_HP_MODIFIER ) + PLC_DUMMY_HP_MODIFIER );
    // initiate "check for session abort/cancel"
    // this function polls every 5 sec and checks if the player is still
    // fighting with the dummy. if not, session gets aborted
    // and dummy gets re-created. this deletes all variables and resets
    // everything to clean state.
    // this "pseudo heartbeat" only runs during PC<>dummy interaction
    // and is very small
    DelayCommand(5.0,DummyCheckSession(oDummy));
//    FloatingTextStringOnCreature("Session Started - Dummy Strength: "+IntToString(GetLocalInt(oDummy,"PLC_INTERACT_SESSIONHP")),oPC);
//    if(GetLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP"))
//      FloatingTextStringOnCreature("You won't get XP this time",oPC);
  }

  object oUser = GetLocalObject(oDummy,"PLC_INTERACT_USER");
  if(oUser == oPC)
  {
    int nSessionHP = GetLocalInt(oDummy,"PLC_INTERACT_SESSIONHP");
    int nTotalDamage = GetMaxHitPoints() - GetCurrentHitPoints();
    int nHitCount;
    int nLastTotalDamage = GetLocalInt(oDummy,"PLC_INTERACT_LASTHP");
    nHitCount = GetLocalInt(oDummy,"PLC_INTERACT_HITCOUNT");

    // register hit
    if(nTotalDamage > nLastTotalDamage)
    {
      SetLocalInt(oDummy,"PLC_INTERACT_LASTHP", nTotalDamage);
      nHitCount++;
      SetLocalInt(oDummy,"PLC_INTERACT_HITCOUNT", nHitCount);
    }

    // apply some effects based on damage
    int nDamage = nTotalDamage - nLastTotalDamage;
    if (nDamage > 24) ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(75), OBJECT_SELF);
    if (nDamage > 12) ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(281), OBJECT_SELF);
    if (nDamage > 6)  ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(112), OBJECT_SELF);
    if (nDamage > 3)  ApplyEffectToObject(DURATION_TYPE_INSTANT,EffectVisualEffect(236 + d3()), OBJECT_SELF);
    SendMessageToPC(oPC, IntToString(nTotalDamage) + " damage, " +  IntToString(nHitCount) + " hits.");

    if(nTotalDamage > nSessionHP) // session done - dummy HP depleted
    {
      if (GetLocalInt(OBJECT_SELF,"DO_ONCE") == 1) return;
      SetLocalInt(OBJECT_SELF,"DO_ONCE",1);

      FloatingTextStringOnCreature("You have finished your training session",oPC);
      // report final statistic
      int nAvgDam = nTotalDamage/nHitCount;
      AssignCommand(GetModule(),DelayCommand(2.0,FloatingTextStringOnCreature("You inflicted "+IntToString(nTotalDamage)+ " damage to the dummy with "+IntToString(nHitCount)+" hits",oPC)));
      AssignCommand(GetModule(),DelayCommand(4.0,FloatingTextStringOnCreature("Your average damage is " + IntToString(nAvgDam)+" per attack",oPC)));

      int nIdx;
      int nRank;
      int nHolder=0;
      int nAvgDamHolder;
      string sNameHolder;
      for (nIdx = 10; nIdx > 0;  nIdx--)
      {
        nHolder = GetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nIdx));
        //debug SendMessageToPC(oPC, IntToString(nIdx)+"="+IntToString(nHolder));
        if (nAvgDam > nHolder) nRank = nIdx;
      }
      //debug SendMessageToPC(oPC, "Rank="+IntToString(nRank));

      if (nRank < 11)
      AssignCommand(GetModule(),DelayCommand(6.0,FloatingTextStringOnCreature("Your score ranks #"+IntToString(nRank)+" on the Best Bobo Bashers list.", oPC)));


      // save stats in campaign db

      for (nIdx = 9; nIdx >= nRank; nIdx--)
      {
         nAvgDamHolder = GetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nIdx));
         sNameHolder = GetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nIdx));
         SetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nIdx+1), nAvgDamHolder);
         SetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nIdx+1), sNameHolder);
         //debug SendMessageToPC(oPC, IntToString(nIdx+1)+") Set Name="+sNameHolder+", Set AvgDam="+IntToString(nAvgDamHolder));
      }
      SetCampaignInt("PGC3_DATA", "BOBO_AVGDAM_"+IntToString(nRank), nAvgDam);
      //SetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nRank), GetName(oPC));

      string sWeaponName = "bare hands";
      if (GetLastWeaponUsed(oPC) != OBJECT_INVALID) sWeaponName = GetName(GetLastWeaponUsed(oPC));
      SetCampaignString("PGC3_DATA", "BOBO_NAME_"+IntToString(nRank), GetName(oPC) + ", with " + sWeaponName);

      // award xp only if permitted
//      if(GetLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP"))
//        AssignCommand(GetModule(),DelayCommand(8.0,FloatingTextStringOnCreature("No XP reward ! You trained too recently...",oPC)));
//      else
//      {
//        // award xp and report it to the user
//        AssignCommand(GetModule(),DelayCommand(8.0,FloatingTextStringOnCreature("You earned " + IntToString( FloatToInt( IntToFloat(nSessionHP) * PLC_DUMMY_XP_MODIFIER ) ) + " xp for your training",oPC)));
//        AssignCommand(GetModule(),DelayCommand(9.0,GiveXPToCreature(oPC,FloatToInt( IntToFloat(nSessionHP)* PLC_DUMMY_XP_MODIFIER ) ) ) );
        // set no-xp flag
//        SetLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP",TRUE);
        // set deletion timer for no-xp flag
//        AssignCommand(GetModule(),DelayCommand(PLC_DUMMY_XP_TIMER,DeleteLocalInt(oPC,"PLC_INTERACT_COMBATDUMMY_NOXP")));
//      }
      // destroy & re-create dummy
      string sResRef = GetResRef(oDummy); location lLoc = GetLocation(oDummy);
      AssignCommand(GetModule(),DelayCommand(0.1,DummyRespawn(sResRef,lLoc)));;
      DestroyObject(oDummy);
    }
  }
  else
    SendMessageToPC(oPC,"Bobo is busy now.");
}

void main()
{
  object oLastDamager = GetLastDamager();

  if(GetIsObjectValid(oLastDamager))
  {
    //SendMessageToPC(GetFirstPC(),"Last Damager: "+GetName(oLastDamager));
    DummyMain(oLastDamager,OBJECT_SELF);
  }
}
