// hcr 3  8/12/2003
// fix for subrace and pwexp taking away fatigue and exhaustion.
// fix for xp message for _NXP tag creatures.
// work around for bw bug where dms are pcs.
// remarked out killing npc twice because xp message is still appearing.
// added x0 script.
// dont give xp for summoned or familiar creatures as they are part of the master.
// sr6.1
// PW fix by Catscan
// sr6.0
// added check to not award xp if creatures tag contains _NXP.
// added new xp code to fix multiple bugs with old system.
// added multiple henchmen code for xp and only awarding
// half xp to henchmen (actually just giving more to pcs).
// 5.5.3
// 5.5.2
// changed bypass code to use OBJECT_SELF
// 5.5.1
// Added code to bypass creating second corpse due to killing
// fixed respawn code to not skip respawns due to fix above.
// the npc twice.
// 5.4
// Thanks to Lanthar added destroying npc items if npc corpse system is off,
// else npc corpse system will take care of destroying items.
// Set XP fixed for trainers
// Took out default animal corpses now they are used with npc corpse system.
// 5.3
// Added respawn code if you want a monster to respawn
//  put RESPAWN in its tag name. If you want it to respawn
//  at a certain point add a waypoint with same tag name
//  as monster. If you want to use random spawn points add
//  waypoints with same tag name as monster + 1 to 10.
//  Respawn timer can be changed if desired. if you want
//  monsters to respawn with different delays use multiple
//  scripts.
//::///////////////////////////////////////////////
//:: Default:On Death
//:: NW_C2_DEFAULT7
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
Shouts to allies that they have been killed
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Oct 25, 2001
//:://////////////////////////////////////////////

// hcr3 added xo script remarked out generic as its not used.
#include "x0_i0_spawncond"
#include "hc_inc"
#include "subraces"
// sr6.0
#include "hc_inc_exp"
// hcr3 8/12/2003
#include "hc_inc_fatigue"

void ClearSlot(int iSlotID)
{
    object oItem = GetItemInSlot(iSlotID);
    if (GetIsObjectValid(oItem) && !GetDroppableFlag(oItem))
        DestroyObject(oItem);
}

void RespawnObject(string sResRef, int iType, location lLoc, float fFacing, string sNewTag) {
object oRespawn = CreateObject(iType, sResRef, lLoc);
object oNew = CopyObject(oRespawn, lLoc, OBJECT_INVALID, sNewTag);
DestroyObject(oRespawn);
AssignCommand(oNew, ActionDoCommand(SetFacing(fFacing)));
SetLocalInt(GetModule(), "ONCE", 1);
}

void main()
{
 // 5.5.1 added code to avoid creating 2 corpses
 // 5.5.2 changed variable to object self to prevent corpses dissapearing.
 // HCR3 remarked out code as only killing once.
 /*if (GetLocalInt(OBJECT_SELF, "CORPSE"))
    {
       SetLocalInt(OBJECT_SELF, "CORPSE", FALSE);
       return;
    }
 SetLocalInt(OBJECT_SELF, "CORPSE", TRUE);
 */
 string sResRef = GetResRef(OBJECT_SELF);
 int iType = GetObjectType(OBJECT_SELF);
 // For creatures, set a waypoint with same tag name at spawn point and
 // use it instead. Otherwise, the creature will respawn where it died.
 float fFacing;
 location lLoc;
 int iRandom = d10(1);
 string sTag = GetTag(OBJECT_SELF);
 object oWaypoint = GetWaypointByTag(sTag + IntToString(iRandom));
 fFacing = GetFacing(oWaypoint);
 if (FindSubString(sTag, "RESPAWN") > -1 )
 {
 if (GetIsObjectValid(oWaypoint))
    lLoc = GetLocation(oWaypoint);
 else
 {
   oWaypoint = GetWaypointByTag(GetTag(OBJECT_SELF));
   fFacing = GetFacing(oWaypoint);
   if (GetIsObjectValid(oWaypoint))
     lLoc = GetLocation(oWaypoint);
   else
   {
     //hcr3 change so npcs spawn at same point without using waypoints.
     lLoc = GetLocalLocation(OBJECT_SELF,"spawn");// *here*
     DeleteLocalInt(OBJECT_SELF, "spawn");
     fFacing = GetFacing(OBJECT_SELF);
   }
 }
 // the following is a random time to respawn, to use uncomment line below comment the second line.
 //float fDelay = IntToFloat(d100(3) + 60);
 float fDelay = 160.0;  //300.0 = 5 minute delay; adjust as desired
 AssignCommand(GetModule(), DelayCommand(fDelay, RespawnObject(sResRef, iType, lLoc, fFacing, sTag)));
 }


   object oKiller = GetLastKiller();
   // Get a controlled undead's master
   if(GetLocalString(oKiller, "MY_MASTER_IS") != "") {

        string sName = GetLocalString(oKiller, "MY_MASTER_IS");
        object oPC = GetFirstPC();
        while(GetIsObjectValid(oPC)) {

            if(sName == GetPCPlayerName(oPC)) {
                oKiller = oPC;
            }
            oPC = GetNextPC();
        }
    }
    int nClass = GetLevelByClass(CLASS_TYPE_COMMONER);
    int nAlign = GetAlignmentGoodEvil(OBJECT_SELF);
    if(nClass > 0 && (nAlign == ALIGNMENT_GOOD || nAlign == ALIGNMENT_NEUTRAL))
    {
        AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5);
    }

    SpeakString("NW_I_AM_DEAD", TALKVOLUME_SILENT_TALK);
//Shout Attack my target, only works with the On Spawn In setup
    SpeakString("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK);

    if(GetSpawnInCondition(NW_FLAG_DEATH_EVENT))
    {
        SignalEvent(OBJECT_SELF, EventUserDefined(1007));
    }

    if (GetLocalInt(oMod, "NPCCORPSE"))
           ExecuteScript("hc_npccorpse", OBJECT_SELF);
    else {
      // Destroy all equipped slots
      int iSlotID;
      for (iSlotID = 0; iSlotID < NUM_INVENTORY_SLOTS; iSlotID++) {
        ClearSlot(iSlotID);
      }
      // Destroy all inventory items
      object oItem = GetFirstItemInInventory();
      while (GetIsObjectValid(oItem)) {
        if (!GetDroppableFlag(oItem))
            DestroyObject(oItem);
        oItem = GetNextItemInInventory();
      }
    }
    int nBaseExp=GetLocalInt(oMod,"BASEXP");
    // hcr3 7/27/2003
    if(!GetLocalInt(oMod, "HCREXP")) return;

// Beginning of XP Gen mod:

    // hcr3 7/19/2003
    if (GetIsDM(GetLastKiller()))
         return;

    // hcr3 dont give xp for summoned or familiar creatures.
    if (GetIsObjectValid(GetMaster(OBJECT_SELF)))
    {
        object oMaster = GetMaster(OBJECT_SELF);
        object oAssociate = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oMaster);
        if (oAssociate == OBJECT_SELF)
            return;
        oAssociate = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oMaster);
        if (oAssociate == OBJECT_SELF)
            return;
    }

    float fCR = GetChallengeRating(OBJECT_SELF);
    int nMonsterXP;


// Get number of party members, and average Party Level

    int nPartyMembers;
    int nPartyLevelSum;
    // sr6.0 new xp code.
    int nHenchmen =0;
    int nHighLevel = 0;
    float fAvgPartyLevel;


    object oPC = GetFirstFactionMember(oKiller);
    // sr6.0 if tag has no xp then dont allow xp.
    // hcr3 8/12/2003 fix because you can not get rid of the default xp message anymore.
    if (FindSubString(GetTag(OBJECT_SELF), "_NXP") > -1)
    {
        SendMessageToPC(oPC,"You gain no experience from this kill");
        return;
    }
    object oKilledArea = GetArea(OBJECT_SELF);
    // hcr3 took out check for area if your in a party they deduct your xp
    // no matter if they are in your area or not!
    while(GetIsObjectValid(oPC))
    {
        int nDM=GetIsDM(oPC);
        nPartyMembers++;
        if(GetIsObjectValid(GetHenchman(oPC)) && !nDM)
        {
                //sr6.0 fix for multiple henchmen per pc
                object oHench = GetHenchman(oPC);
                while (GetIsObjectValid(oHench))
                {
                  nPartyMembers++;
                  nPartyLevelSum += GetHitDice(oHench);
                  if (GetHitDice(oHench) > nHighLevel)
                      nHighLevel = GetHitDice(oHench);
                  nHenchmen++;
                  oHench = GetHenchman(oHench);
                }
        }
        // sr6.0 fix for adjusting levels if they are more than 4 apart.
        int nAdd = 0;
        if (GetHitDice(oPC) > nHighLevel)
            nHighLevel = GetHitDice(oPC);
        else if (nHighLevel - GetHitDice(oPC) > 4)
             nAdd = nHighLevel;
        if(nDM) nPartyMembers--;
        else if (nAdd == 0) nPartyLevelSum += GetHitDice(oPC);
        else    nPartyLevelSum += nAdd;
        //SendMessageToPC(oPC, "PartyLevel "+IntToString(nPartyLevelSum));
        //SendMessageToPC(oPC, "PartyMembers "+IntToString(nPartyMembers));
        oPC = GetNextFactionMember(oKiller, TRUE);
    }

    if (nPartyMembers == 0)
        return;
    fAvgPartyLevel = IntToFloat(nPartyLevelSum) / IntToFloat(nPartyMembers);

    // sr6.0
    int nLevel = FloatToInt(fAvgPartyLevel);
    nMonsterXP = UseXPTable(nBaseExp, fCR, nLevel);
    nMonsterXP += GetLocalInt(oMod,"BONUSXP");
    int nCharXP = nMonsterXP / nPartyMembers;
    // sr6.0 if CR is less than 1 multiply xp by cr.
    if (fCR < 1.0)
       nCharXP = FloatToInt(nCharXP * fCR);
    // sr 6.0 fix for henchmen getting only 3/4 xp for kills rest goes to party.
           if (nHenchmen > 0)
           {
             int nHenchmenbonus = ((nCharXP*nHenchmen)/8) / (nPartyMembers-nHenchmen);
             nCharXP = nCharXP + nHenchmenbonus;
           }


    oPC = GetFirstFactionMember(oKiller);
    while(GetIsObjectValid(oPC))
    {
        if(!GetIsDM(oPC) && oKilledArea == GetArea(oPC))
        {
           // sr6.0 discourages grouping of higher levels with lower
           // levels to get xp. this is as per ad hoc suggestions in DMG.
           //SendMessageToPC(oPC, "Monster XP "+IntToString(nMonsterXP));
           nLevel = GetHitDice(oPC);
           nMonsterXP = UseXPTable(nBaseExp, fCR, nLevel);
           nMonsterXP += GetLocalInt(oMod,"BONUSXP");
           int nSCharXP = nMonsterXP / nPartyMembers;
           // sr6.0 if CR is less than 1 multiply xp by cr.
           if (fCR < 1.0)
              nSCharXP = FloatToInt(nSCharXP * fCR);
           // sr 6.0 fix for henchmen getting only 3/4 xp for kills rest goes to party.
           if (nHenchmen > 0)
           {
             int nHenchmenbonus = ((nSCharXP*nHenchmen)/8) / (nPartyMembers-nHenchmen);
             nSCharXP = nSCharXP + nHenchmenbonus;
           }
           // sr6.0 party xp should never be more than single xp.
           if (nCharXP > nSCharXP && nPartyMembers > 1)
               nCharXP = nSCharXP;
            // sr6.0 should never get xp if 7+cr levels above cr.
            int nCR = 0;
            if (fCR < 1.0)
                nCR = 1;
            else
                nCR = FloatToInt(fCR);
            if ((GetHitDice(oPC) - nCR) >= (7+nCR))
                nCharXP = 0;
            int nCurLvl=GetHitDice(oPC);
            int nExpPen=GetLocalInt(GetModule(),"REZPEN"+GetName(oPC)+
                GetPCPlayerName(oPC));

            // ADDED BY CATSCAN: Reasoning: use local variable to hold
            //                   this value instead of recalculating
            //                   Adds to code clarity.
            int nExpNextLvl = (((nCurLvl+1) * nCurLvl) / 2 * 1000) - 1;
            // END ADDED BY CATSCAN

            // hcr3 8/12/2003
            // workaround for subrace bug and PWXP bug where they would clear out all
            // extraordinary effects including Fatigue and Exhaustion when using
            // the level trainer. it now works correctly.

            int nSUBRACES = GetLocalInt(oMod, "SUBRACES");
            int nPWEXP = GetLocalInt(oMod, "PWEXP");
            int nXP  = GetXP(oPC);
            // hcr3 8/12/2003
            if (nSUBRACES || nPWEXP) // subrace and pwexp system
            {
              if(!GetLocalInt(oMod,"LEVELTRAINER"))
              {
                XP_RewardXP(oPC, nCharXP);
                if (nCharXP == 0 && nPartyMembers == 1)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "you are too far advanced in level");
                else if (nCharXP == 0)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "your party is too far advanced in level");
              }
              else if(nExpPen)
              {
                if(nCharXP > nExpPen)
                {
                    SendMessageToPC(oPC,"You have paid off your exp penalty.");
                    DeleteLocalInt(GetModule(),"REZPEN"+GetName(oPC)+
                        GetPCPlayerName(oPC));
                }
                SetLocalInt(GetModule(),"REZPEN"+GetName(oPC)+
                    GetPCPlayerName(oPC),nExpPen-nCharXP);
              }
              // BEGIN MODIFIED BY CATSCAN:  Changed check from GetXP(oPC) + nCharXP
              //       to use XP_GetXPReward which returns what the XP level would
              //       be set to.
              //       Replaced all occurances of '(((nCurLvl+1) * nCurLvl) / 2 * 1000) - 1'
              //       with nExpNextLvl defined above
              else if( (XP_GetXPReward(oPC, nCharXP)) > nExpNextLvl)
              {
                SetXP(oPC, nExpNextLvl);
                if (nExpNextLvl <= GetXP(oPC))
                   SendMessageToPC(oPC,"You learn nothing from this kill, perhaps "+
                      "you should seek out training.");
                else
                   SendMessageToPC(oPC,"You learn little from this kill, perhaps "+
                    "you should seek out training.");
                if(GetLocalInt(GetModule(),"PWEXP"))
                {
                    SetUpExp(oPC, 0);
                }
              }
              // END MODIFIED BY CATSCAN
              else
              {
                XP_RewardXP(oPC, nCharXP);
                if (nCharXP == 0 && nPartyMembers == 1)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "you are too far advanced in level");
                else if (nCharXP == 0)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "your party is too far advanced in level");
              }
              // hcr3 8/12/2003 reapply effects of exhausted and fatigued.
              if (GetLocalInt(oPC,"bExhausted"))
              {
                SetLocalInt(oPC, "bExhausted", FALSE);
                MakePlayerExhausted(oPC, "");
              }
              else if (GetLocalInt(oPC,"bFatigued"))
              {
                SetLocalInt(oPC, "bFatigued", FALSE);
                MakePlayerFatigued(oPC, "");
              }
            } // end of subraces and pwexp code.

            // hcr3 8/12/2003
            else // regular xp code note you dont level then have to relevel with trainer as above.
            {
              if(!GetLocalInt(oMod,"LEVELTRAINER"))
              {
                if (nCharXP > 0)
                   GiveXPToCreature(oPC, nCharXP);
                if (nCharXP == 0 && nPartyMembers == 1)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "you are too far advanced in level");
                else if (nCharXP == 0)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "your party is too far advanced in level");
              }
              else if(nExpPen)
              {
                if(nCharXP > nExpPen)
                {
                    SendMessageToPC(oPC,"You have paid off your exp penalty.");
                    DeleteLocalInt(GetModule(),"REZPEN"+GetName(oPC)+
                        GetPCPlayerName(oPC));
                }
                SetLocalInt(GetModule(),"REZPEN"+GetName(oPC)+
                    GetPCPlayerName(oPC),nExpPen-nCharXP);
              }
              else if( (nXP + nCharXP) > nExpNextLvl)
              {
                //int nXp =(((nCurLvl+1) * nCurLvl) / 2 * 1000)-1;
                if (nExpNextLvl <= GetXP(oPC))
                   SendMessageToPC(oPC,"You learn nothing from this kill, perhaps "+
                      "you should seek out training.");
                else
                {
                   SetXP(oPC, nExpNextLvl);
                   SendMessageToPC(oPC,"You learn little from this kill, perhaps "+
                    "you should seek out training.");
                }
                if(GetLocalInt(GetModule(),"PWEXP"))
                {
                    SetUpExp(oPC, 0);
                }

              }
              else
              {
                if (nCharXP > 0)
                    GiveXPToCreature(oPC, nCharXP);
                if (nCharXP == 0 && nPartyMembers == 1)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "you are too far advanced in level");
                else if (nCharXP == 0)
                  SendMessageToPC(oPC,"You learn nothing from this kill, "+
                   "your party is too far advanced in level");
              }
            }// end of regular xp code.
        }
        oPC = GetNextFactionMember(oKiller, TRUE);

    }
    AdjustReputation (oPC,OBJECT_SELF,50);
    GetNextFactionMember(OBJECT_SELF);
}
