/*/////////////////////// [On Death] /////////////////////////////////////////// Filename: J_AI_OnDeath or nw_c2_default7 ///////////////////////// [On Death] /////////////////////////////////////////// Speeded up no end, when compiling, with seperate Include. Cleans up all un-droppable items, all ints and all local things when destroyed. Check down near the bottom for a good place to add XP or corpse lines ;-) ///////////////////////// [History] //////////////////////////////////////////// 1.3 - Added in Turn of corpses toggle - Added in appropriate space for XP awards, marked with ideas (effect death) 1.4 - Removed the redudnant notes on the "You have gained 0 experience" message ///////////////////////// [Workings] /////////////////////////////////////////// You can edit this for experience, there is a seperate section for it. It will use DeathCheck to execute a cleanup-and-destroy script, that removes any coprse, named "j_ai_destroyself". ///////////////////////// [Arguments] ////////////////////////////////////////// Arguments: GetLastKiller. ///////////////////////// [On Death] /////////////////////////////////////////*/ // We only require the constants/debug file. We have 1 function, not worth another include. #include "J_INC_CONSTANTS" #include "xp_inc" #include "lgs_inc" #include "creature_inc" // We need a wrapper. If the amount of deaths, got in this, is not equal to iDeaths, // we don't execute the script, else we do. :-P void DeathCheck(int nDeaths); void main() { // Set Name (Original Name only) creature_SetName(OBJECT_SELF, CREATURE_NAME_DISPLAY_ORIGINAL); // If we are set to, don't fire this script at all if(GetAIInteger(I_AM_TOTALLY_DEAD)) return; // Pre-death-event. Returns TRUE if we interrupt this script call. if(FirePreUserEvent(AI_FLAG_UDE_DEATH_PRE_EVENT, EVENT_DEATH_PRE_EVENT)) return; // Note: No AI on/off check here. // Who killed us? (alignment changing, debug, XP). object oKiller = GetLastKiller(); // Get a controlled undead's master if(GetLocalString(oKiller, "MY_MASTER_IS") != "") { object oKillerMaster = GetMaster(oKiller); oKiller = oKillerMaster; } if (GetLocalInt(GetModule(),"X3_ENABLE_MOUNT_DB")&&GetIsObjectValid(GetMaster(OBJECT_SELF))) SetLocalInt(GetMaster(OBJECT_SELF),"bX3_STORE_MOUNT_INFO",TRUE); // Stops if we just applied EffectDeath to ourselves. if(GetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH)) return; // Special: To stop giving out multiple amounts of XP, we use EffectDeath // to change the killer, so the XP systems will NOT award MORE XP. // - Even the default one suffers from this! if(GetAIInteger(WE_HAVE_DIED_ONCE)) { if(!GetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH)) { // Don't apply effect death to self more then once per 2 seconds. SetLocalTimer(AI_TIMER_DEATH_EFFECT_DEATH, 2.0); // This should make the last killer us. ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), OBJECT_SELF); ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(GetMaxHitPoints()), OBJECT_SELF); } } else if(oKiller != OBJECT_SELF) { // Set have died once, stops giving out mulitple amounts of XP. SetAIInteger(WE_HAVE_DIED_ONCE, TRUE); /*/////////////////////// [Experience] ///////////////////////////////////////// THIS is the place for it, below this comment. It is useful to use GetFirstFactionMember (and Next), GiveXPToCreature, GetXP, SetXP, GetChallengeRating (of self) all are really useful. Bug note: GetFirstFactionMember/Next with the PC parameter means either ONLY PC, and so NPC henchmen, unless FALSE is used, will not be even recognised. ///////////////////////// [Experience] ///////////////////////////////////////*/ // Do XP things (Use object "oKiller" for who killed us). xp_GiveXPForKill(OBJECT_SELF, oKiller); /*/////////////////////// [Experience] ///////////////////////////////////////*/ } // Note: Here we do a simple way of checking how many times we have died. // Nothing special. Debugging most useful aspect. int nDeathCounterNew = GetAIInteger(AMOUNT_OF_DEATHS); nDeathCounterNew++; SetAIInteger(AMOUNT_OF_DEATHS, nDeathCounterNew); // Here is the last time (in game seconds) we died. It is used in the executed script // to make sure we don't prematurly remove areselves. // We may want some sort of visual effect - like implosion or something, to fire. int nDeathEffect = GetAIConstant(AI_DEATH_VISUAL_EFFECT); // Valid constants from 0 and up. Apply to our location (not to us, who will go!) if(nDeathEffect >= 0) { ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(nDeathEffect), GetLocation(OBJECT_SELF)); } // Default Commoner alignment changing. (If the commoner is not evil!) if(GetLevelByClass(CLASS_TYPE_COMMONER) > 0 && GetAlignmentGoodEvil(OBJECT_SELF) != ALIGNMENT_EVIL && !GetSpawnInCondition(AI_FLAG_OTHER_NO_COMMONER_ALIGNMENT_CHANGE, AI_OTHER_MASTER)) { if(GetIsPC(oKiller)) { AdjustAlignment(oKiller, ALIGNMENT_EVIL, 5); } else { // If it is a summon, henchmen or familar of a PC, we adust the PC itself // Clever, eh? object oMaster = GetMaster(oKiller); if(GetIsObjectValid(oMaster) && GetIsPC(oMaster)) { AdjustAlignment(oMaster, ALIGNMENT_EVIL, 5); } } } // Always shout when we are killed. Reactions - Morale penalty, and // attack the killer. AISpeakString(AI_SHOUT_I_WAS_KILLED); // Speaks the set death speak, like "AGGGGGGGGGGGGGGGGGGG!! NOOOO!" for instance :-) // Note for 1.4: No need for "CanSpeak()" for this, of course. SpeakArrayString(AI_TALK_ON_DEATH); // First check - do we use "destroyable corpses" or not? (default, yes) if(!GetSpawnInCondition(AI_FLAG_OTHER_TURN_OFF_CORPSES, AI_OTHER_MASTER)) { // We will actually dissapear after 30.0 seconds if not raised. int nTime = GetAIInteger(AI_CORPSE_DESTROY_TIME); if(nTime == 0) // Error checking { nTime = 30; } // 64: "[Death] Checking corpse status in " + IntToString(nTime) + " [Killer] " + GetName(oKiller) + " [Times Died Now] " + IntToString(nDeathCounterNew) DebugActionSpeakByInt(64, oKiller, nTime, IntToString(nDeathCounterNew)); // Delay check DelayCommand(IntToFloat(nTime), DeathCheck(nDeathCounterNew)); } else { /************************ [Alternative Corpses] ******************************** This is where you can add some alternative corpse code - EG looting and so on, without disrupting the rest of the AI (as the corpses are turned off). ************************* [Alternative Corpses] *******************************/ // Add alternative corpse code here if(GetRacialType(OBJECT_SELF)==RACIAL_TYPE_ANIMAL) { SetIsDestroyable(FALSE, FALSE, TRUE); DelayCommand(30.0,SetIsDestroyable(TRUE, FALSE, TRUE)); } else { // Get the location of the dead monster location lCorpseLoc = GetLocation(OBJECT_SELF); // Make the corpse NOT fade SetIsDestroyable(FALSE, TRUE, FALSE); // Create Blood Stain under the coprse object oBlood = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_bloodstain", lCorpseLoc, FALSE); // Create Loot Container on the corpse object oLootObject = CreateObject(OBJECT_TYPE_PLACEABLE, "invis_corpse_obj", lCorpseLoc, FALSE, GetTag(OBJECT_SELF)); SetName(oLootObject, "Dead " + GetName(OBJECT_SELF)); SetDescription(oLootObject, GetDescription(OBJECT_SELF)); // Create Loot if ((GetStandardFactionReputation(STANDARD_FACTION_HOSTILE, OBJECT_SELF) >= 90) // Only Faction Hostile creatures will drop stuff //|| FindSubString(GetName(OBJECT_SELF), "Kobold") >= 0 // HACK! Make kobolds drop loot too! || FindSubString(GetName(OBJECT_SELF), "Spider") >= 0) // HACK! Make spiders drop loot too! { int nMinLoot = GetLocalInt(OBJECT_SELF, CS_LGS_LOOT_MINITEMS); if (nMinLoot == 0) nMinLoot = -1; object oContainer = GetNearestObjectByTag(CS_LGS_CHEST_VAR_RESREF, OBJECT_SELF); if (!GetIsObjectValid(oContainer)) { lgs_CreateLoot(OBJECT_SELF, oLootObject, OBJECT_INVALID, FALSE, nMinLoot); } else { lgs_CreateLoot(OBJECT_SELF, oContainer, oLootObject, FALSE, nMinLoot); // Inform the Loot Container to (re)start it's countdown if (GetLocalInt(oContainer, CS_LGS_CHEST_CHECK_ISCHECKING)) SetLocalInt(oContainer, CS_LGS_CHEST_CHECK_COUNT, 1); else ExecuteScript("lgs_chest_check", oContainer); } } // Check if there's anything in the loot container if (!GetIsObjectValid(GetFirstItemInInventory(oLootObject))) DestroyObject(oLootObject); // Plan for everything to decay float fFade = 240.0f; object oModule = GetModule(); ActionWait(fFade); DelayCommand(fFade, lgs_ClearInventory(oLootObject)); DelayCommand(fFade, lgs_ClearInventory(OBJECT_SELF)); DelayCommand(fFade + 0.1f, DestroyObject(oLootObject)); DelayCommand(fFade + 0.1f, DestroyObject(oBlood)); DelayCommand(fFade + 0.3f, SetIsDestroyable(TRUE,TRUE,FALSE)); DelayCommand(fFade + 0.5f, DestroyObject(OBJECT_SELF)); } /************************ [Alternative Corpses] *******************************/ } // Signal the death event. FireUserEvent(AI_FLAG_UDE_DEATH_EVENT, EVENT_DEATH_EVENT); } // We need a wrapper. If the amount of deaths, got in this, is not equal to iDeaths, // we don't execute the script, else we do. :-P void DeathCheck(int nDeaths) { // Do the deaths imputted equal the amount we have suffered? if(GetAIInteger(AMOUNT_OF_DEATHS) == nDeaths) { // - This now includes a check for Bioware's lootable functions and using them. ExecuteScript(FILE_DEATH_CLEANUP, OBJECT_SELF); } }