From d4b97848b2712fcd435bc9c160f0c85092b45821 Mon Sep 17 00:00:00 2001 From: b5635 Date: Fri, 15 Sep 2023 17:49:20 -0700 Subject: [PATCH] fixed meldanen having bio scripts, reworked "unconscious" npc code to allow them to be actually killed and revived after some time --- src/git/blak_meldan2.git.json | 26 +++++++++---------- src/nss/ai_ondamaged.nss | 16 ------------ src/nss/ai_ondeath.nss | 14 ++++++++++ src/nss/ai_onheartb.nss | 48 +++++++++++++++++++++++++---------- src/nss/ai_onspawn.nss | 10 +++++++- src/nss/ai_run.nss | 1 - src/nss/area_refresh.nss | 2 +- src/nss/inc_ai_combat.nss | 12 ++------- src/nss/inc_general.nss | 4 +-- src/nss/pptreas_disturb.nss | 1 - src/nss/x0_i0_spells.nss | 2 +- 11 files changed, 76 insertions(+), 60 deletions(-) diff --git a/src/git/blak_meldan2.git.json b/src/git/blak_meldan2.git.json index c15c79c5..f1e58e02 100644 --- a/src/git/blak_meldan2.git.json +++ b/src/git/blak_meldan2.git.json @@ -1625,55 +1625,55 @@ }, "ScriptAttacked": { "type": "resref", - "value": "nw_c2_default5" + "value": "ai_onattacked" }, "ScriptDamaged": { "type": "resref", - "value": "nw_c2_default6" + "value": "ai_ondamaged" }, "ScriptDeath": { "type": "resref", - "value": "nw_c2_bossdie" + "value": "ai_ondeath" }, "ScriptDialogue": { "type": "resref", - "value": "nw_c2_default4" + "value": "ai_onconverse" }, "ScriptDisturbed": { "type": "resref", - "value": "nw_c2_default8" + "value": "ai_ondisturb" }, "ScriptEndRound": { "type": "resref", - "value": "nw_c2_default3" + "value": "ai_oncombrnd" }, "ScriptHeartbeat": { "type": "resref", - "value": "nw_c2_default1" + "value": "ai_onheartb" }, "ScriptOnBlocked": { "type": "resref", - "value": "nw_c2_defaulte" + "value": "ai_onblocked" }, "ScriptOnNotice": { "type": "resref", - "value": "nw_c2_default2" + "value": "ai_onpercep" }, "ScriptRested": { "type": "resref", - "value": "nw_c2_defaulta" + "value": "ai_onrest" }, "ScriptSpawn": { "type": "resref", - "value": "nw_c2_bossspawn" + "value": "ai_onspawn" }, "ScriptSpellAt": { "type": "resref", - "value": "nw_c2_defaultb" + "value": "ai_onspellcast" }, "ScriptUserDefine": { "type": "resref", - "value": "nw_c2_defaultd" + "value": "ai_onuserdef" }, "SkillList": { "type": "list", diff --git a/src/nss/ai_ondamaged.nss b/src/nss/ai_ondamaged.nss index 431dd737..7ef5f932 100644 --- a/src/nss/ai_ondamaged.nss +++ b/src/nss/ai_ondamaged.nss @@ -67,20 +67,4 @@ void main() { gsCBDetermineCombatRound(oHighestDamager); } - - if (GetImmortal() && GetLocalInt(OBJECT_SELF, "unconscious") == 0) - { - int nKnockdownHP = GetMaxHitPoints()/10; - if (nKnockdownHP < 1) - nKnockdownHP = 1; - if (GetCurrentHitPoints() <= nKnockdownHP) - { - effect eKnockdown = EffectKnockdown(); - float fDuration = 60.0; - //FloatingTextStringOnCreature(GetName(OBJECT_SELF)+" is now unconscious.", OBJECT_SELF, FALSE); - SetLocalInt(OBJECT_SELF, "unconscious", 1); - ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eKnockdown, OBJECT_SELF, fDuration); - DelayCommand(fDuration, DeleteLocalInt(OBJECT_SELF, "unconscious")); - } - } } diff --git a/src/nss/ai_ondeath.nss b/src/nss/ai_ondeath.nss index 023d4ee4..239afc65 100644 --- a/src/nss/ai_ondeath.nss +++ b/src/nss/ai_ondeath.nss @@ -7,6 +7,13 @@ #include "inc_webhook" #include "nwnx_area" +void ReviveSelf() +{ + ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), OBJECT_SELF); + ForceRest(OBJECT_SELF); + DeleteLocalInt(OBJECT_SELF, "combat"); +} + void main() { SignalEvent(OBJECT_SELF, EventUserDefined(GS_EV_ON_DEATH)); @@ -164,6 +171,13 @@ void main() string sScript = GetLocalString(OBJECT_SELF, "death_script"); if (sScript != "") ExecuteScript(sScript); + if (GetLocalInt(OBJECT_SELF, "immortal") == 1) + { + DelayCommand(IntToFloat(120+Random(121)), ReviveSelf()); + + return; // do not do any gibbing code + } + if (GibsNPC(OBJECT_SELF)) { DoMoraleCheckSphere(OBJECT_SELF, MORALE_PANIC_GIB_DC); diff --git a/src/nss/ai_onheartb.nss b/src/nss/ai_onheartb.nss index 7d61da86..9068bc67 100644 --- a/src/nss/ai_onheartb.nss +++ b/src/nss/ai_onheartb.nss @@ -136,7 +136,7 @@ void main() if (GetLocalInt(OBJECT_SELF, "ambush") == 1) { // only attack or move to the ambush location when not in combat, otherwise combat can be interrupted - if (!GetIsInCombat(OBJECT_SELF)) + if (!nCombat) { object oTarget = GetLocalObject(OBJECT_SELF, "ambush_target"); @@ -160,21 +160,43 @@ void main() { // return to the original spawn point if it is too far location lSpawn = GetLocalLocation(OBJECT_SELF, "spawn"); - float fDistanceFromSpawn = GetDistanceBetweenLocations(GetLocation(OBJECT_SELF), lSpawn); - float fMaxDistance = 5.0; + object oSpawnArea = GetAreaFromLocation(lSpawn); + +// if not in the same area, force the NPC to jump back to the spawn location after a while + if (GetIsObjectValid(oSpawnArea) && oSpawnArea != GetArea(OBJECT_SELF)) + { + int nNotInSameAreaCount = GetLocalInt(OBJECT_SELF, "not_in_same_area_count"); + if (!nCombat && nNotInSameAreaCount >= 25) + { + ClearAllActions(); + ActionJumpToLocation(lSpawn); + DeleteLocalInt(OBJECT_SELF, "not_in_same_area_count"); + } + else + { + SetLocalInt(OBJECT_SELF, "not_in_same_area_count", nNotInSameAreaCount + 1); + } + } + else + { + DeleteLocalInt(OBJECT_SELF, "not_in_same_area_count"); + + float fDistanceFromSpawn = GetDistanceBetweenLocations(GetLocation(OBJECT_SELF), lSpawn); + float fMaxDistance = 5.0; - if (GetLocalString(OBJECT_SELF, "merchant") != "") fMaxDistance = fMaxDistance * 0.5; + if (GetLocalString(OBJECT_SELF, "merchant") != "") fMaxDistance = fMaxDistance * 0.5; -// enemies and herbivores have a much farther distance before they need to reset - if ((GetStandardFactionReputation(STANDARD_FACTION_DEFENDER, OBJECT_SELF) <= 10) || GetLocalInt(OBJECT_SELF, "herbivore") == 1) fMaxDistance = fMaxDistance*10.0; + // enemies and herbivores have a much farther distance before they need to reset + if ((GetStandardFactionReputation(STANDARD_FACTION_DEFENDER, OBJECT_SELF) <= 10) || GetLocalInt(OBJECT_SELF, "herbivore") == 1) fMaxDistance = fMaxDistance*10.0; - if (GetLocalInt(OBJECT_SELF, "no_wander") == 1) fMaxDistance = 0.0; -// Not in combat? Different/Invalid area? Too far from spawn? - if (GetLocalInt(OBJECT_SELF, "ambient") != 1 && !nCombat && !bBusy && ((fDistanceFromSpawn == -1.0) || (fDistanceFromSpawn > fMaxDistance))) - { - AssignCommand(OBJECT_SELF, ClearAllActions()); - MoveToNewLocation(lSpawn, OBJECT_SELF); - return; + if (GetLocalInt(OBJECT_SELF, "no_wander") == 1) fMaxDistance = 0.0; + // Not in combat? Different/Invalid area? Too far from spawn? + if (GetLocalInt(OBJECT_SELF, "ambient") != 1 && !nCombat && !bBusy && ((fDistanceFromSpawn == -1.0) || (fDistanceFromSpawn > fMaxDistance))) + { + ClearAllActions(); + MoveToNewLocation(lSpawn, OBJECT_SELF); + return; + } } } diff --git a/src/nss/ai_onspawn.nss b/src/nss/ai_onspawn.nss index 61010f74..fb39f5b9 100644 --- a/src/nss/ai_onspawn.nss +++ b/src/nss/ai_onspawn.nss @@ -54,6 +54,14 @@ void main() DetermineMaxHitPoints(OBJECT_SELF); + if (GetImmortal(OBJECT_SELF)) + { + SetLocalInt(OBJECT_SELF, "immortal", 1); + SetLocalInt(OBJECT_SELF, "no_credit", 1); + SetImmortal(OBJECT_SELF, FALSE); + SetIsDestroyable(FALSE, TRUE, FALSE); + } + //listen SetListenPattern(OBJECT_SELF, "GS_AI_ATTACK_TARGET", 10000); SetListenPattern(OBJECT_SELF, "GS_AI_REQUEST_REINFORCEMENT", 10003); @@ -106,7 +114,7 @@ void main() if (d3() == 1) GeneratePickpocketItem(); } // 2x the gold on semibosses or immortals (quest/unique npcs usually) - else if (GetLocalInt(OBJECT_SELF, "semiboss") == 1 || GetLocalInt(OBJECT_SELF, "rare") || GetImmortal(OBJECT_SELF)) + else if (GetLocalInt(OBJECT_SELF, "semiboss") == 1 || GetLocalInt(OBJECT_SELF, "rare") || GetLocalInt(OBJECT_SELF, "immortal") == 1) { nGold = nGold * 2; GeneratePickpocketItem("Misc"); diff --git a/src/nss/ai_run.nss b/src/nss/ai_run.nss index 3e155896..30f6eb1c 100644 --- a/src/nss/ai_run.nss +++ b/src/nss/ai_run.nss @@ -55,7 +55,6 @@ void main() GetLocalInt(OBJECT_SELF, "no_rest") == 0 && !GetIsInCombat(OBJECT_SELF) && !gsC2GetHasEffect(EFFECT_TYPE_PETRIFY, OBJECT_SELF) && - GetLocalInt(OBJECT_SELF, "unconscious") == 0 && GetLocalInt(OBJECT_SELF, "combat") > 3 && !GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE))) { diff --git a/src/nss/area_refresh.nss b/src/nss/area_refresh.nss index e0d3f21a..db985476 100644 --- a/src/nss/area_refresh.nss +++ b/src/nss/area_refresh.nss @@ -274,7 +274,7 @@ void main() // Add to quest npc list if required int nFaction = NWNX_Creature_GetFaction(oCreature); - if (GetPlotFlag(oCreature) || GetImmortal(oCreature) || nFaction == STANDARD_FACTION_COMMONER || nFaction == STANDARD_FACTION_DEFENDER || nFaction == STANDARD_FACTION_MERCHANT) + if (GetPlotFlag(oCreature) || GetImmortal(oCreature) || GetLocalInt(oCreature, "immortal") == 1 || nFaction == STANDARD_FACTION_COMMONER || nFaction == STANDARD_FACTION_DEFENDER || nFaction == STANDARD_FACTION_MERCHANT) { string sQuest = GetLocalString(oCreature, "quest1"); if (sQuest != "") diff --git a/src/nss/inc_ai_combat.nss b/src/nss/inc_ai_combat.nss index f9c44389..aed615ea 100644 --- a/src/nss/inc_ai_combat.nss +++ b/src/nss/inc_ai_combat.nss @@ -678,7 +678,7 @@ object gsCBGetAttackTarget(object oObject = OBJECT_SELF, object oTarget = OBJECT CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); if (! GetIsObjectValid(oTarget) || - GetPlotFlag(oTarget) || GetLocalInt(oTarget, "unconscious") == 1) + GetPlotFlag(oTarget)) { oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oObject, 1, @@ -686,7 +686,7 @@ object gsCBGetAttackTarget(object oObject = OBJECT_SELF, object oTarget = OBJECT CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD); if (! GetIsObjectValid(oTarget) || - GetPlotFlag(oTarget) || GetLocalInt(oTarget, "unconscious") == 1) + GetPlotFlag(oTarget)) { gsCBClearAttackTarget(oObject); return OBJECT_INVALID; @@ -912,14 +912,6 @@ void gsCBDetermineCombatRound(object oTarget = OBJECT_INVALID) return; } -// dont attack unconscious targets - pok - if (GetLocalInt(oTarget, "unconscious") == 1) - { - ClearAllActions(); - return; - } - - // Turn off Detect if there is a valid target SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, FALSE); diff --git a/src/nss/inc_general.nss b/src/nss/inc_general.nss index 78b3425e..f8167731 100644 --- a/src/nss/inc_general.nss +++ b/src/nss/inc_general.nss @@ -636,14 +636,12 @@ void DetermineMaxHitPoints(object oCreature) { if (GetIsPC(oCreature)) return; - // these are considered plot or quest NPCs, keep the default - if (GetImmortal(oCreature)) return; if (GetPlotFlag(oCreature)) return; // don't do this for pets if (GetAssociateType(oCreature) == ASSOCIATE_TYPE_FAMILIAR || GetAssociateType(oCreature) == ASSOCIATE_TYPE_ANIMALCOMPANION) return; - int bMaximize = GetLocalInt(oCreature, "boss") == 1 || GetLocalInt(oCreature, "semiboss") == 1 || GetLocalInt(oCreature, "rare") == 1; + int bMaximize = GetImmortal(oCreature) || GetLocalInt(oCreature, "boss") == 1 || GetLocalInt(oCreature, "semiboss") == 1 || GetLocalInt(oCreature, "rare") == 1; int nHP = GetHitPointsByClassPosition(oCreature, 1, bMaximize) + GetHitPointsByClassPosition(oCreature, 2, bMaximize) + GetHitPointsByClassPosition(oCreature, 3, bMaximize); diff --git a/src/nss/pptreas_disturb.nss b/src/nss/pptreas_disturb.nss index 01fa127e..30027e84 100644 --- a/src/nss/pptreas_disturb.nss +++ b/src/nss/pptreas_disturb.nss @@ -6,7 +6,6 @@ int ValidThiefChecker(object oTarget, object oThief) { if (GetIsPC(oTarget)) return FALSE; if (GetIsDead(oTarget)) return FALSE; - if (GetLocalInt(oTarget, "unconscious") == 1) return FALSE; if (GetAssociateType(oTarget) > 0 ) return FALSE; //if (GetStandardFactionReputation(STANDARD_FACTION_COMMONER, OBJECT_SELF) <= 50 && GetStandardFactionReputation(STANDARD_FACTION_DEFENDER, OBJECT_SELF) <= 50) return FALSE; if (!GetObjectSeen(oThief, oTarget) && !LineOfSightObject(oThief, oTarget)) return FALSE; diff --git a/src/nss/x0_i0_spells.nss b/src/nss/x0_i0_spells.nss index 46eeb74b..eb424c81 100644 --- a/src/nss/x0_i0_spells.nss +++ b/src/nss/x0_i0_spells.nss @@ -907,7 +907,7 @@ void DoCaltropEffect(object oTarget) int CanCreatureBeDestroyed(object oTarget) { - return !GetPlotFlag(oTarget) && !GetImmortal(oTarget); + return !GetPlotFlag(oTarget) && !GetImmortal(oTarget) && GetLocalInt(oTarget, "immortal") != 1; } //*GZ: 2003-07-23. honor critical and weapon spec