/* Henchman Inventory And Battle AI This file contains functions used in the default On* scripts for combat. It acts as filter preventing the main hench_o0_ai from being called more than it needs to. (Usually only to start combat, heartbeat, or end combat round.) */ #include "hench_i0_generic" #include "hench_i0_melee" // void main() { } // Sets the location of an unheard or unseen enemy // Either moved out of range or did attack while not detected void SetEnemyLocation(object oEnemy); // Clears the last unheard, unseen enemy location void ClearEnemyLocation(); // Moves to the last perceived enemy location void MoveToLastSeenOrHeard(int bDoSearch = TRUE); // New determinecombatround call. Determines if call should cause // main AI script to run void HenchDetermineCombatRound(object oIntruder = OBJECT_INVALID, int bForceInterrupt = FALSE); // Simplified combat start, used by commoners void HenchStartAttack(object oIntruder); // modified TalentFlee routine int HenchTalentFlee(object oIntruder = OBJECT_INVALID); // modified GetNearestSeenOrHeardEnemy to not get dead creatures object GetNearestSeenOrHeardEnemyNotDead(int bCheckIsPC = FALSE); // modified DetermineSpecialBehavior, calls HenchDetermineCombatRound instead void HenchDetermineSpecialBehavior(object oIntruder = OBJECT_INVALID); // used to attack non creature items (placeables and doors) void HenchAttackObject(object oTarget); // associate (henchman, etc.) determines if enemy is perceived or not int HenchGetIsEnemyPerceived(); // modified TalentAdvancedBuff routine int HenchTalentAdvancedBuff(float fDistance); // get nearest ally that is higher in hit dice object GetNearestTougherFriend(object oSelf, object oPC); // main stealth and wandering code int DoStealthAndWander(); // checks if stealth should be removed because you or nearby friend has been detected void CheckRemoveStealth(); // code for bashing placeables int HenchBashDoorCheck(int bPolymorphed); const int HENCH_AI_SCRIPT_NOT_RUN = 0; const int HENCH_AI_SCRIPT_IN_PROGRESS = 1; const int HENCH_AI_SCRIPT_ALREADY_RUN = 2; const string HENCH_AI_SCRIPT_RUN_STATE = "AIIntruder"; const string HENCH_AI_SCRIPT_FORCE = "AIIntruderForce"; const string HENCH_AI_SCRIPT_INTRUDER_OBJ = "AIIntruderObj"; const string sHenchLastHeardOrSeen = "LastSeenOrHeard"; void SetEnemyLocation(object oEnemy) { SetLocalInt(OBJECT_SELF, sHenchLastHeardOrSeen, TRUE); location enemyLocation = GetLocation(oEnemy); SetLocalLocation(OBJECT_SELF, sHenchLastHeardOrSeen, enemyLocation); // Get the area, position and facing object oArea = GetAreaFromLocation (enemyLocation); vector vPosition = GetPositionFromLocation (enemyLocation); float fFacing = GetFacingFromLocation (enemyLocation); vPosition.z += 100.0; // Build a new location, which faces in the opposite direction enemyLocation = Location (oArea, vPosition, fFacing); // make sure old position is gone object oInvisTarget = GetLocalObject(OBJECT_SELF, sHenchLastHeardOrSeen); if (GetIsObjectValid(oInvisTarget)) { DestroyObject(oInvisTarget); DeleteLocalObject(OBJECT_SELF, sHenchLastHeardOrSeen); } oInvisTarget = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_invisobj", enemyLocation); SetLocalObject(OBJECT_SELF, sHenchLastHeardOrSeen, oInvisTarget); } void ClearEnemyLocation() { DeleteLocalInt(OBJECT_SELF, sHenchLastHeardOrSeen); DeleteLocalLocation(OBJECT_SELF, sHenchLastHeardOrSeen); object oInvisTarget = GetLocalObject(OBJECT_SELF, sHenchLastHeardOrSeen); if (GetIsObjectValid(oInvisTarget)) { DestroyObject(oInvisTarget); DeleteLocalObject(OBJECT_SELF, sHenchLastHeardOrSeen); } } void MoveToLastSeenOrHeard(int bDoSearch = TRUE) { location moveToLoc = GetLocalLocation(OBJECT_SELF, sHenchLastHeardOrSeen); if (GetDistanceBetweenLocations(GetLocation(OBJECT_SELF), moveToLoc) <= 5.0) { ClearEnemyLocation(); // go to search // TODO add spells to help search DeleteLocalInt(OBJECT_SELF, HENCH_AI_SCRIPT_RUN_STATE); if (bDoSearch) { // search around for awhile ActionDoCommand(SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE)); ActionRandomWalk(); DelayCommand(10.0, HenchDetermineCombatRound()); } } else { object oInvisTarget = GetLocalObject(OBJECT_SELF, sHenchLastHeardOrSeen); ActionMoveToObject(oInvisTarget, TRUE); } } void HenchDetermineCombatRound(object oIntruder = OBJECT_INVALID, int bForceInterrupt = FALSE) { // Jug_Debug(GetName(OBJECT_SELF) + " det com round called with " + GetName(oIntruder) + " force = " + IntToString(bForceInterrupt)); if(GetAssociateState(NW_ASC_IS_BUSY)) { return; } string sAIScript = GetLocalString(OBJECT_SELF, "AIScript"); if (sAIScript == "") { sAIScript = "hench_o0_ai"; } SetLocalObject(OBJECT_SELF, HENCH_AI_SCRIPT_INTRUDER_OBJ, oIntruder); SetLocalInt(OBJECT_SELF, HENCH_AI_SCRIPT_FORCE, bForceInterrupt); if (bForceInterrupt) { SetLocalInt(OBJECT_SELF, HENCH_AI_SCRIPT_RUN_STATE, HENCH_AI_SCRIPT_IN_PROGRESS); ExecuteScript(sAIScript, OBJECT_SELF); return; } // check if we have to actually determine to rerun ai int iAIIntruderLevel = GetLocalInt(OBJECT_SELF, HENCH_AI_SCRIPT_RUN_STATE); if (iAIIntruderLevel == HENCH_AI_SCRIPT_NOT_RUN) { // first run of HenchDetermineCombatRound SetLocalInt(OBJECT_SELF, HENCH_AI_SCRIPT_RUN_STATE, HENCH_AI_SCRIPT_IN_PROGRESS); ExecuteScript(sAIScript, OBJECT_SELF); return; } object oLastTarget = GetLocalObject(OBJECT_SELF, sHenchLastTarget); if(!GetIsObjectValid(oLastTarget) || GetIsDead(oLastTarget) || !GetIsEnemy(oLastTarget) || GetLocalInt(oLastTarget, sHenchRunningAway)) { oLastTarget = OBJECT_INVALID; } else if (!GetObjectSeen(oLastTarget) && !GetObjectHeard(oLastTarget)) { oLastTarget = OBJECT_INVALID; } if (!GetIsObjectValid(oLastTarget)) { // prevent too many calls to main script if already moving to an unseen and // unheard monster if (!GetLocalInt(OBJECT_SELF, sHenchLastHeardOrSeen) || GetObjectSeen(oIntruder) || GetObjectHeard(oIntruder)) { ExecuteScript(sAIScript, OBJECT_SELF); } } else if (GetIsObjectValid(oIntruder)) { if (GetDistanceToObject(oIntruder) <= 5.0 && GetDistanceToObject(oLastTarget) > 5.0) { ExecuteScript(sAIScript, OBJECT_SELF); } else if (GetObjectSeen(oIntruder) && !GetObjectSeen(oLastTarget)) { ExecuteScript(sAIScript, OBJECT_SELF); } } } void HenchStartAttack(object oIntruder) { SetLocalObject(OBJECT_SELF, HENCH_AI_SCRIPT_INTRUDER_OBJ, oIntruder); ExecuteScript("hench_o0_att", OBJECT_SELF); } // FLEE COMBAT AND HOSTILES int HenchTalentFlee(object oIntruder = OBJECT_INVALID) { object oTarget = oIntruder; if(!GetIsObjectValid(oIntruder)) { oTarget = GetLastHostileActor(); if(!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) { oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); float fDist = GetDistanceBetween(OBJECT_SELF, oTarget); if(!GetIsObjectValid(oTarget)) { return FALSE; } } } ClearAllActions(); //Look at this to remove the delay ActionMoveAwayFromObject(oTarget, TRUE, 10.0f); DelayCommand(4.0, ClearAllActions()); return TRUE; } object GetNearestSeenOrHeardEnemyNotDead(int bCheckIsPC = FALSE) { int curCount = 1; object oTarget; while (1) { oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, curCount, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); if (!GetIsObjectValid(oTarget)) { break; } if (!GetPlotFlag(oTarget)) { return oTarget; } curCount ++; } curCount = 1; while (1) { oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, curCount, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); if (!GetIsObjectValid(oTarget)) { return OBJECT_INVALID; } if (!GetPlotFlag(oTarget) && !(bCheckIsPC && GetIsPC(GetTopMaster(oTarget)))) { return oTarget; } curCount++; } return OBJECT_INVALID; } //:://///////////////////////////////////////////// //:: Determine Special Behavior //:: Copyright (c) 2001 Bioware Corp. //::////////////////////////////////////////////// /* Determines the special behavior used by the NPC. Generally all NPCs who you want to behave differently than the default behavior. For these behaviors, passing in a valid object will cause the creature to become hostile the the attacker. MODIFIED February 7 2003: - Rearranged logic order a little so that the creatures will actually randomwalk when not fighting - Modified by Tony K to call HenchDetermineCombatRound - Modified by LoCash (Jan 12-Mar 06 2004): BW's original function was completely broken, it needed to be re-written. original code by "fendis_khan". many enhancements made. herbivores now work "out of the box" as they should, omnivores need faction changed to a non-Hostile. Check "Animals" in the Toolset's Monster palette for various examples of creatures using omnivore, herbivore spawn scripts. */ //::////////////////////////////////////////////// //:: Created By: Preston Watamaniuk //:: Created On: Dec 14, 2001 //::////////////////////////////////////////////// void HenchDetermineSpecialBehavior(object oIntruder = OBJECT_INVALID) { object oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); // Omnivore behavior routine if(GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE)) { // no current attacker and not currently in combat if(!GetIsObjectValid(oIntruder) && !GetIsInCombat()) { // does not have a current target if(!GetIsObjectValid(GetAttemptedAttackTarget()) && !GetIsObjectValid(GetAttemptedSpellTarget()) && !GetIsObjectValid(GetAttackTarget())) { // enemy creature nearby if(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 13.0) { ClearAllActions(); HenchDetermineCombatRound(oTarget); return; } int nTarget = 1; oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, nTarget, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_NEUTRAL); // neutral creature, too close while(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 7.0) { if(GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0 && GetAssociateType(oTarget) != ASSOCIATE_TYPE_ANIMALCOMPANION) { // oTarget has neutral reputation, and is NOT a druid or ranger or an "Animal Companion" SetLocalInt(OBJECT_SELF, "lcTempEnemy", 8); SetIsTemporaryEnemy(oTarget); ClearAllActions(); HenchDetermineCombatRound(oTarget); return; } oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, ++nTarget, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_NEUTRAL); } // non friend creature, too close nTarget = 1; oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN, OBJECT_SELF, nTarget); // heard neutral or enemy creature, too close while(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 7.0) { if(!GetIsFriend(oTarget) && GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0 && GetAssociateType(oTarget) != ASSOCIATE_TYPE_ANIMALCOMPANION) { // oTarget has neutral reputation, and is NOT a druid or ranger or an "Animal Companion" SetLocalInt(OBJECT_SELF, "lcTempEnemy", 8); SetIsTemporaryEnemy(oTarget); ClearAllActions(); HenchDetermineCombatRound(oTarget); return; } oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN, OBJECT_SELF, ++nTarget); } if(!IsInConversation(OBJECT_SELF)) { // 25% chance of just standing around instead of constantly // randWalking; i thought it looked odd seeing the animal(s) // in a constant state of movement, was not realistic, // at least according to my Nat'l Geographic videos if ( (d4() != 1) && (GetCurrentAction() == ACTION_RANDOMWALK) ) { return; } else if ( (d4() == 1) && (GetCurrentAction() == ACTION_RANDOMWALK) ) { ClearAllActions(); return; } else { ClearAllActions(); ActionRandomWalk(); return; } } } } else if(!IsInConversation(OBJECT_SELF)) // enter combat when attacked { // after a while (20-25 seconds), omnivore (boar) "gives up" // chasing someone who didn't hurt it. but if the person fought back // this condition won't run and the boar will fight to death if(GetLocalInt(OBJECT_SELF, "lcTempEnemy") != FALSE && (GetLastDamager() == OBJECT_INVALID || GetLastDamager() != oTarget) ) { int nPatience = GetLocalInt(OBJECT_SELF, "lcTempEnemy"); if (nPatience <= 1) { ClearAllActions(); ClearPersonalReputation(oTarget); // reset reputation DeleteLocalInt(OBJECT_SELF, "lcTempEnemy"); return; } SetLocalInt(OBJECT_SELF, "lcTempEnemy", --nPatience); } ClearAllActions(); HenchDetermineCombatRound(oIntruder); } } // Herbivore behavior routine else if(GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE)) { // no current attacker & not currently in combat if(!GetIsObjectValid(oIntruder) && (GetIsInCombat() == FALSE)) { if(!GetIsObjectValid(GetAttemptedAttackTarget()) && // does not have a current target !GetIsObjectValid(GetAttemptedSpellTarget()) && !GetIsObjectValid(GetAttackTarget())) { if(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 13.0) // enemy creature, too close { ClearAllActions(); ActionMoveAwayFromObject(oTarget, TRUE, 16.0); // flee from enemy return; } int nTarget = 1; oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, nTarget, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_NEUTRAL); while(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 7.0) // only consider close creatures { if(GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0 && GetAssociateType(oTarget) != ASSOCIATE_TYPE_ANIMALCOMPANION) { // oTarget has neutral reputation, and is NOT a druid or ranger or Animal Companion ClearAllActions(); ActionMoveAwayFromObject(oTarget, TRUE, 16.0); // run away return; } oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, ++nTarget, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_NEUTRAL); } nTarget = 1; oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN, OBJECT_SELF, nTarget); while(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 7.0) // only consider close creatures { if(!GetIsFriend(oTarget) && GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0 && GetAssociateType(oTarget) != ASSOCIATE_TYPE_ANIMALCOMPANION) { // oTarget has neutral reputation, and is NOT a druid or ranger or Animal Companion ClearAllActions(); ActionMoveAwayFromObject(oTarget, TRUE, 16.0); // run away return; } oTarget = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN, OBJECT_SELF, ++nTarget); } if(!IsInConversation(OBJECT_SELF)) { // 75% chance of randomWalking around, 25% chance of just standing there. more realistic if ( (d4() != 1) && (GetCurrentAction() == ACTION_RANDOMWALK) ) { return; } else if ( (d4() == 1) && (GetCurrentAction() == ACTION_RANDOMWALK) ) { ClearAllActions(); return; } else { ClearAllActions(); ActionRandomWalk(); return; } } } } else if(!IsInConversation(OBJECT_SELF)) // NEW BEHAVIOR - run away when attacked { ClearAllActions(); ActionMoveAwayFromLocation(GetLocation(OBJECT_SELF), TRUE, 16.0); } } } void HenchAttackObject(object oTarget) { if (GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND))) { UseCombatAttack(oTarget); } else if(GetHasFeat(FEAT_IMPROVED_POWER_ATTACK)) { UseCombatAttack(oTarget, FEAT_IMPROVED_POWER_ATTACK); } else if(GetHasFeat(FEAT_POWER_ATTACK)) { UseCombatAttack(oTarget, FEAT_POWER_ATTACK); } else if(GetHasFeat(FEAT_DIRTY_FIGHTING)) { UseCombatAttack(oTarget, FEAT_DIRTY_FIGHTING); } else { UseCombatAttack(oTarget); } } int HenchGetIsEnemyPerceived() { object oClosestSeen = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); if (GetIsObjectValid(oClosestSeen)) { return TRUE; } object oClosestHeard = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD_AND_NOT_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); if (GetIsObjectValid(oClosestHeard)) { if (GetDistanceToObject(oClosestHeard) <= fHenchHearingDistance) { return TRUE; } object oRealMaster = GetRealMaster(); if (GetIsObjectValid(oRealMaster)) { if (GetDistanceBetween(oRealMaster, oClosestHeard) <= fHenchMasterHearingDistance) { return TRUE; } } } return FALSE; } // FAST BUFF SELF int HenchTalentAdvancedBuff(float fDistance) { object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); if(GetIsObjectValid(oPC) && GetDistanceToObject(oPC) <= fDistance) { if(!GetIsFighting(OBJECT_SELF)) { ClearAllActions(); //Combat Protections if(GetHasSpell(SPELL_PREMONITION)) { ActionCastSpellAtObject(SPELL_PREMONITION, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_GREATER_STONESKIN)) { ActionCastSpellAtObject(SPELL_GREATER_STONESKIN, OBJECT_SELF, METAMAGIC_NONE, 0, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_STONESKIN)) { ActionCastSpellAtObject(SPELL_STONESKIN, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } //Visage Protections if(GetHasSpell(SPELL_SHADOW_SHIELD)) { ActionCastSpellAtObject(SPELL_SHADOW_SHIELD, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_ETHEREAL_VISAGE)) { ActionCastSpellAtObject(SPELL_ETHEREAL_VISAGE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_GHOSTLY_VISAGE)) { ActionCastSpellAtObject(SPELL_GHOSTLY_VISAGE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } //Mantle Protections if(GetHasSpell(SPELL_GREATER_SPELL_MANTLE)) { ActionCastSpellAtObject(SPELL_GREATER_SPELL_MANTLE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SPELL_MANTLE)) { ActionCastSpellAtObject(SPELL_SPELL_MANTLE, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_LESSER_SPELL_BREACH)) { ActionCastSpellAtObject(SPELL_LESSER_SPELL_BREACH, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } // Globes if(GetHasSpell(SPELL_GLOBE_OF_INVULNERABILITY)) { ActionCastSpellAtObject(SPELL_GLOBE_OF_INVULNERABILITY, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_MINOR_GLOBE_OF_INVULNERABILITY)) { ActionCastSpellAtObject(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } //Misc Protections if(GetHasSpell(SPELL_ELEMENTAL_SHIELD)) { ActionCastSpellAtObject(SPELL_ELEMENTAL_SHIELD, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if (GetHasSpell(SPELL_MESTILS_ACID_SHEATH)&& !GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH)) { ActionCastSpellAtObject(SPELL_MESTILS_ACID_SHEATH, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if (GetHasSpell(SPELL_DEATH_ARMOR)&& !GetHasSpellEffect(SPELL_DEATH_ARMOR)) { ActionCastSpellAtObject(SPELL_DEATH_ARMOR, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } //Elemental Protections if(GetHasSpell(SPELL_PROTECTION_FROM_ELEMENTS)) { ActionCastSpellAtObject(SPELL_PROTECTION_FROM_ELEMENTS, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_RESIST_ELEMENTS)) { ActionCastSpellAtObject(SPELL_RESIST_ELEMENTS, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_ENDURE_ELEMENTS)) { ActionCastSpellAtObject(SPELL_ENDURE_ELEMENTS, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } //Mental Protections if(GetHasSpell(SPELL_MIND_BLANK)) { ActionCastSpellAtObject(SPELL_MIND_BLANK, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_LESSER_MIND_BLANK)) { ActionCastSpellAtObject(SPELL_LESSER_MIND_BLANK, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_CLARITY)) { ActionCastSpellAtObject(SPELL_CLARITY, OBJECT_SELF, METAMAGIC_NONE, FALSE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } //Summon Ally // TODO add gate!!!! if(GetHasSpell(SPELL_BLACK_BLADE_OF_DISASTER)) { ActionCastSpellAtLocation(SPELL_BLACK_BLADE_OF_DISASTER, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_ELEMENTAL_SWARM)) { ActionCastSpellAtLocation(SPELL_ELEMENTAL_SWARM, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_IX)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_IX, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_CREATE_GREATER_UNDEAD)) { ActionCastSpellAtLocation(SPELL_CREATE_GREATER_UNDEAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_GREATER_PLANAR_BINDING)) { ActionCastSpellAtLocation(SPELL_GREATER_PLANAR_BINDING, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_VIII)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_VIII, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_MORDENKAINENS_SWORD)) { ActionCastSpellAtLocation(SPELL_MORDENKAINENS_SWORD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_VII)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_VII, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_CREATE_UNDEAD)) { ActionCastSpellAtLocation(SPELL_CREATE_UNDEAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_PLANAR_BINDING)) { ActionCastSpellAtLocation(SPELL_PLANAR_BINDING, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_VI)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_VI, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_V)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_V, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELLABILITY_SUMMON_SLAAD)) { ActionCastSpellAtLocation(SPELLABILITY_SUMMON_SLAAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELLABILITY_SUMMON_TANARRI)) { ActionCastSpellAtLocation(SPELLABILITY_SUMMON_TANARRI, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELLABILITY_SUMMON_MEPHIT)) { ActionCastSpellAtLocation(SPELLABILITY_SUMMON_MEPHIT, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELLABILITY_SUMMON_CELESTIAL)) { ActionCastSpellAtLocation(SPELLABILITY_SUMMON_CELESTIAL, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_ANIMATE_DEAD)) { ActionCastSpellAtLocation(SPELL_ANIMATE_DEAD, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_IV)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_IV, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_III)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_III, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_II)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_II, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } else if(GetHasSpell(SPELL_SUMMON_CREATURE_I)) { ActionCastSpellAtLocation(SPELL_SUMMON_CREATURE_I, GetLocation(OBJECT_SELF), METAMAGIC_NONE, FALSE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); } return TRUE; } } return FALSE; } object GetNearestTougherFriend(object oSelf, object oPC) { int i = 0; object oFriend = oSelf; int nEqual = 0; int nNear = 0; while (GetIsObjectValid(oFriend)) { if (GetDistanceBetween(oSelf, oFriend) < 40.0 && oFriend != oSelf) { ++nNear; if (GetHitDice(oFriend) > GetHitDice(oSelf)) return oFriend; if (GetHitDice(oFriend) == GetHitDice(oSelf)) ++nEqual; } ++i; oFriend = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, oSelf, i); } SetLocalInt(OBJECT_SELF,"LocalBoss",FALSE); if (nEqual == 0) if (nNear > 0 || GetHitDice(oPC) - GetHitDice(OBJECT_SELF) < 2) { SetLocalInt(OBJECT_SELF,"LocalBoss",TRUE); } return OBJECT_INVALID; } // Auldar: Configurable distance at which to "hide", if the PC, or PC's Associate is within that distance. // TK 40 doesn't seem to be far enough const float stealthDistThresh = 80.0; // Pausanias: monsters try to find you. int DoStealthAndWander() { if (GetPlotFlag(OBJECT_SELF)) { return FALSE; } int nStealthAndWander = GetMonsterOptions(HENCH_MONAI_STEALTH | HENCH_MONAI_WANDER); if (!nStealthAndWander) { return FALSE; } // Auldar: and they now stealth if they have some skill points (and not marked with plot flag) object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); object oNearestHostile = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); if (!(GetIsObjectValid(oNearestHostile) && GetIsObjectValid(oPC) && GetIsEnemy(oPC))) { return FALSE; } int bActionsCleared = FALSE; if ((nStealthAndWander & HENCH_MONAI_STEALTH) && !GetPlotFlag(OBJECT_SELF) && !GetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH)) { // Auldar: Checking if the NPC is hostile to the PC and has skill points in Hide // or move silently, and it not marked Plot. If so, go stealthy even if not flagged // by the module creator as Stealth on spawn, as long as the PC or associate is hostile and close. if (CheckStealth()) { // Auldar: Check how far away the nearest hostile Creature is float enemyDistance = GetDistanceToObject(oNearestHostile); // Auldar: and check if it's a PC, or a PC's associate. object oRealHostileMaster = GetRealMaster(oNearestHostile); if (((oPC == oNearestHostile) || (GetIsObjectValid(oRealHostileMaster) && GetIsPC(oRealHostileMaster) && GetIsEnemy(oRealHostileMaster))) && (enemyDistance <= stealthDistThresh) && (enemyDistance != -1.0)) { ClearAllActions(); bActionsCleared = TRUE; SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE); } } // Auldar: here ends Auldar's NPC stealth code. Back to Paus' work :) } // Auldar: Reducing the distance from 40.0 to 25.0 to reduce the "bloodbath" effect // requested by FoxBat. if ((nStealthAndWander & HENCH_MONAI_WANDER) && GetDistanceToObject(oPC) < 25.0) { int ScoutMode = GetLocalInt(OBJECT_SELF,"ScoutMode"); if (ScoutMode == 0) { ScoutMode = d2(); SetLocalInt(OBJECT_SELF,"ScoutMode",ScoutMode); } object oTarget = GetNearestTougherFriend(OBJECT_SELF,oPC); if (!GetLocalInt(OBJECT_SELF,"LocalBoss")) { int fDist = 15; if (!GetIsObjectValid(oTarget) || ScoutMode == 1) { fDist = 10; oTarget = oPC; if (d10() > 5) fDist = 25; } location lNew; if (GetLocalInt(OBJECT_SELF,"OpenedDoor")) { lNew = GetLocalLocation(OBJECT_SELF,"ScoutZone"); SetLocalInt(OBJECT_SELF,"OpenedDoor",FALSE); } else { vector vLoc = GetPosition(oTarget); vLoc.x += fDist-IntToFloat(Random(2*fDist+1)); vLoc.y += fDist-IntToFloat(Random(2*fDist+1)); vLoc.z += fDist-IntToFloat(Random(2*fDist+1)); lNew = Location(GetArea(oTarget),vLoc,0.); SetLocalLocation(OBJECT_SELF,"ScoutZone",lNew); } if (!bActionsCleared) { ClearAllActions(); } ActionMoveToLocation(lNew); return TRUE; } } return FALSE; } void CheckRemoveStealth() { if (GetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH)) { int iCheckStealthAmount = GetSkillRank(SKILL_HIDE) + GetSkillRank(SKILL_MOVE_SILENTLY) + 5; SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE); location testLocation = GetLocation(OBJECT_SELF); object oCreature = GetFirstObjectInShape(SHAPE_SPHERE, 15.0, testLocation, TRUE); while(GetIsObjectValid(oCreature)) { if (GetActionMode(oCreature, ACTION_MODE_STEALTH) && GetIsFriend(oCreature) && !GetIsPC(oCreature)) { if (GetSkillRank(SKILL_HIDE, oCreature) + GetSkillRank(SKILL_MOVE_SILENTLY, oCreature) <= iCheckStealthAmount) { // Jug_Debug(GetName(OBJECT_SELF) + " turning off stealth for + " + GetName(oCreature)); SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); } } oCreature = GetNextObjectInShape(SHAPE_SPHERE, 15.0, testLocation, TRUE); } } } // Pausanias's version of the last: float GetEnemyChallenge() // My forumla: Total Challenge of Enemy = log ( Sum (2**challenge) ) // Auldar: Changed to 1.5 at Paus' request to better mirror the 3E DMG float GetEnemyChallenge(object oRelativeTo=OBJECT_SELF) { float fChallenge = 0.; object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 20.0, GetLocation(OBJECT_SELF), TRUE); while(GetIsObjectValid(oTarget)) { if (GetIsEnemy(oTarget)) { if (GetObjectSeen(oTarget) || GetObjectHeard(oTarget)) { // if (!GetIsDisabled(oTarget)) { fChallenge += pow(1.5, GetChallengeRating(oTarget)); } } } oTarget = GetNextObjectInShape(SHAPE_SPHERE, 20.0, GetLocation(OBJECT_SELF), TRUE); } return (log(fChallenge)/log(1.5)) - (IntToFloat(GetHitDice(oRelativeTo)) * HENCH_HITDICE_TO_CR); } //:://///////////////////////////////////////////// //:: Bash Doors //:: Copyright (c) 2002 Bioware Corp. //::////////////////////////////////////////////// /* Used in DetermineCombatRound to keep a henchmen bashing doors. */ //::////////////////////////////////////////////// //:: Created By: Preston Watamaniuk //:: Created On: April 4, 2002 //::////////////////////////////////////////////// int HenchBashDoorCheck(int bPolymorphed) { int bDoor = FALSE; //This code is here to make sure that henchmen keep bashing doors and placeables. object oDoor = GetLocalObject(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH"); if(GetIsObjectValid(oDoor)) { int nDoorMax = GetMaxHitPoints(oDoor); int nDoorNow = GetCurrentHitPoints(oDoor); int nCnt = GetLocalInt(OBJECT_SELF,"NW_GENERIC_DOOR_TO_BASH_HP"); if(GetLocked(oDoor) || GetIsTrapped(oDoor)) { if(nDoorMax == nDoorNow) { nCnt++; SetLocalInt(OBJECT_SELF,"NW_GENERIC_DOOR_TO_BASH_HP", nCnt); } if(nCnt <= 2) { bDoor = TRUE; HenchAttackObject(oDoor); } } if(!bDoor) { DeleteLocalObject(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH"); DeleteLocalInt(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH_HP"); VoiceCuss(); ActionDoCommand(HenchEquipDefaultWeapons()); } } return bDoor; } void HenchStartRangedBashDoor(object oDoor) { ActionEquipMostDamagingRanged(oDoor); if (GetDistanceToObject(oDoor) < 5.0) { ActionMoveAwayFromObject(oDoor, FALSE, 5.0); } else { ActionWait(0.5); } ActionAttack(oDoor); SetLocalObject(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH", oDoor); }