//////////////////////////////////////////////////////////////////////////////// // // Olander's AI // oai_inc_ai // by Don Anderson // dandersonru@msn.com // // Main AI Script // //////////////////////////////////////////////////////////////////////////////// #include "oai_inc_base" //Combat Round Analyzer void OAI_DetermineCombatRound(object oIntruder = OBJECT_INVALID); // * Henchman/associate combat round wrapper // * passing in OBJECT_INVALID is okay // * Does special stuff for henchmen and familiars, then // * falls back to default generic combat code. void HenchmenCombatRound(object oIntruder=OBJECT_INVALID); // Handles responses to henchmen commands, including both radial // menu and voice commands. void bkRespondToHenchmenShout(object oShouter, int nShoutIndex, object oIntruder = OBJECT_INVALID, int nBanInventory=FALSE); //From Tony K's Henchman AI void OAI_DetermineSpecialBehavior(object oTarget = OBJECT_INVALID); //Morale Chance to Attack //Code by Ray Miller 4/21/2003 int DetermineChanceOfAttack(object oPerceived); //Gets Enemy CR Rating Morale Checks //(From Jeff Petersen's Fight or Flight v1.4) float GetEnemyCR(object oEnemy, float fEnemyCR); //Adds up all the HD or CRs from enemies within fRange. //(From Jeff Petersen's Fight or Flight v1.4) float GetTheirCR(object oEnemy, float f_TheirCR, float fRange, object oTheirs); //Determines CR of caller's party by scanning for all //friends within fRange. Does NOT include any friends that //have retreated. //(From Jeff Petersen's Fight or Flight v1.4) float GetOurCR(float f_OurCR, float fRange, object oMine); //Main Setup from Spawn Script //(From Jeff Petersen's Fight or Flight v1.4) void FOF_Set(object oCreature = OBJECT_SELF); //Main Fight or Flight Execute Function //(From Jeff Petersen's Fight or Flight v1.4) void FOF_Execute(object oCreature = OBJECT_SELF); //Leaders Call For Help When Damaged //(From Jeff Petersen's Fight or Flight v1.4) void CallForHelp(); //The act of retreating. //Looks for leaders or rallying points and makes for those, //when present. Otherwise, they run all over the place. //(From Jeff Petersen's Fight or Flight v1.4) void SignalFlee(object oEnemy, float fRange); //Guard Checking For Weapons if Waypoint "DisallowWeapons" is present. void OAI_GuardCheck(object oNPC); //Barmaids void OAI_Barmaid(object oNPC); //Bar Chatter void OAI_BarChatter(object oNPC); //Inn/Restaurant Cooks (Original By Remi Ennd) void OAI_Cooks(object oNPC); //Strippers =) (Original By Aiden and Spectre) void OAI_Stripper(object oNPC); /******************************************************************************/ //:: OAI FUNCTIONS void OAI_DetermineCombatRound(object oIntruder = OBJECT_INVALID) { object oSelf = OBJECT_SELF; //Pre-Checks if(GetIsDead(oSelf)) return; if(!GetCommandable(oSelf)) return; object oLastTarget = GetAttackTarget(); object oBestTarget = OAI_AcquireTarget(); //Might be Switching Targets if(oBestTarget != oLastTarget) { oIntruder = oBestTarget; } //TRUE if Intruder is Valid int HaveEnemy = GetIsObjectValid(oIntruder); if(HaveEnemy) { SetLocalObject(oSelf,"OAI_ATTACK_TARGET",oIntruder); DeleteLocalInt(OBJECT_SELF, "OAI_SEARCHING"); // dk: Clear any search } if(GetCurrentAction() == ACTION_RANDOMWALK) ClearAllActions(); //Cast Triggers and Break Illusions On Start Of Combat //Cast OnSpawn Triggers or Break Illusions if(!GetBattleCondition(OAI_TRIGGER_HAS_BEEN_CAST) && HaveEnemy) { if(BreakIllusion(oIntruder)) return; CastTriggers(); } //Rezzing is the highest priority of all... if(DetermineAllyToRez()) return; //Summon Hordes....Setup in Spawn Script if(HaveEnemy) { SummonHordes(); } //Heal Self if Less Than Half of Our HP's int nStatus = GetCurrentHitPoints(oSelf); int nBase = GetMaxHitPoints(oSelf); if(nStatus < nBase) { if(TalentHealingSelf(TRUE) == TRUE) return; } /******************************************************************************/ //:: SPECIAL COMBAT ROLES //Dragons if((GetBattleCondition(OAI_ROLE_DRAGON) || GetRacialType(oSelf) == RACIAL_TYPE_DRAGON) && HaveEnemy) { if (SpecialDragonStuff(oIntruder)) return; } //Beholders if(GetBattleCondition(OAI_ROLE_BEHOLDER) && HaveEnemy) { SpecialBeholderStuff(oIntruder); return; } //Gelatinous Cubes if(GetBattleCondition(OAI_ROLE_GELATINOUSCUBE) && HaveEnemy) { SpecialGelatinousCubeStuff(oIntruder); return; } //Spiders if(GetBattleCondition(OAI_ROLE_SPIDER) && HaveEnemy) { //50% OR Heavily Wounded int nInjured = FloatToInt(IntToFloat(GetMaxHitPoints()) * 0.3); int nCHP = GetCurrentHitPoints(); if(Random(100) < 50 || nCHP < nInjured) { if(OAI_SpiderTactics(oIntruder)) return; } } //Mindflayers if(GetBattleCondition(OAI_ROLE_MINDFLAYER) && HaveEnemy) { if(OAI_MindFlayerCombat(oIntruder)) return; } //:: SPECIAL COMBAT ROLES /******************************************************************************/ /******************************************************************************/ //:: SPECIAL COMBAT TACTICS (FROM BIOWARE & OLANDER) //Special Ranged Tactics (Special Combat AI Run Through Ranged Combat) if(GetBattleCondition(OAI_COMBAT_FLAG_RANGED) && HaveEnemy) { RangedCombat(oIntruder); return; } //Special Defensive Tactics if(GetBattleCondition(OAI_COMBAT_FLAG_DEFENSIVE) && HaveEnemy) { DetermineCombatRound(oIntruder); return; } //Special Ambusher Tactics if(GetBattleCondition(OAI_COMBAT_FLAG_AMBUSHER) && HaveEnemy) { DetermineCombatRound(oIntruder); return; } //Special Cowardly Tactics if(GetBattleCondition(OAI_COMBAT_FLAG_COWARDLY) && HaveEnemy) { DetermineCombatRound(oIntruder); return; } //Shapechanger With Appearance Set in Spawn Script if(GetLocalInt(OBJECT_SELF, "OAI_SHAPECHANGE") > 0) { if((Random(70) + 30) > ((GetCurrentHitPoints() * 100) / GetMaxHitPoints())) { // We won't polymorph if already so if(!GetHasEffect(EFFECT_TYPE_POLYMORPH, OBJECT_SELF)) { effect eShape = SupernaturalEffect(EffectPolymorph(GetLocalInt(OBJECT_SELF, "OAI_SHAPECHANGE") - 1)); effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD); DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eShape, OBJECT_SELF)); DelayCommand(1.0, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetLocation(OBJECT_SELF))); } DeleteLocalInt(OBJECT_SELF, "OAI_SHAPECHANGE"); SetStatusCondition(OAI_I_CAN_POLYMORPH,FALSE); } } //Palemasters Always Try to Fight like a Palemaster if(GetStatusCondition(OAI_I_CAN_PM_MELEE) && HaveEnemy) { if(GetIsTargetInMeleeRange(oIntruder)) { MeleePalemaster(oIntruder); return; } } //Shapechanger With Feats if(GetStatusCondition(OAI_I_CAN_POLYMORPH) && HaveEnemy) { MeleeShifter(oIntruder); return; } //Summon Help First If Attacker is Close if(GetStatusCondition(OAI_I_CAN_SUMMON) && HaveEnemy) { if(TalentSummonAllies() == TRUE) { SetStatusCondition(OAI_I_CAN_SUMMON, FALSE); } } //:: SPECIAL COMBAT TACTICS (FROM BIOWARE) /******************************************************************************/ /******************************************************************************/ //:: SPELLCASTER ROLES //Role Healer if(GetBattleCondition(OAI_ROLE_HEALER) && HaveEnemy) { BufferCaster(oIntruder,1); return; } //Role Buffer if(GetBattleCondition(OAI_ROLE_BUFFER) && HaveEnemy) { //Buffer Start at State 2 BufferCaster(oIntruder,2); return; } //Role Status Caster if(GetBattleCondition(OAI_ROLE_STATUS_CASTER) && HaveEnemy) { StatusCaster(oIntruder,1); return; } //Role AOE Specialist if(GetBattleCondition(OAI_ROLE_AOE_SPECIALIST) && HaveEnemy) { AOECaster(oIntruder,1); return; } //Role Attack Caster if(GetBattleCondition(OAI_ROLE_ATTACK_CASTER) && HaveEnemy) { AttackCaster(oIntruder,1); return; } //General Caster Combat if(GetStatusCondition(OAI_I_CAN_CAST_SPELLS) && HaveEnemy) { DetermineCombatRound(oIntruder); return; } //:: SPELLCASTER ROLES /******************************************************************************/ /******************************************************************************/ //:: MELEE COMBAT if((GetStatusCondition(OAI_I_CAN_DEX_MELEE) || GetStatusCondition(OAI_I_CAN_FIGHT_MELEE) || GetStatusCondition(OAI_I_CAN_FIGHT_RANGED) || GetStatusCondition(OAI_I_CAN_MONK_MELEE) || GetStatusCondition(OAI_I_CAN_RELIGIOUS_MELEE) || GetStatusCondition(OAI_I_CAN_SHIFTER_MELEE) || GetStatusCondition(OAI_I_CAN_SMITE_MELEE) || GetStatusCondition(OAI_I_CAN_SNEAK_MELEE) || GetStatusCondition(OAI_I_CAN_WM_MELEE)) && HaveEnemy) { OAI_Fighting(oIntruder); return; } //:: MELEE COMBAT /******************************************************************************/ /******************************************************************************/ //:: NWN DETERMINE COMBAT ROUND //Fall Through for Creatures With No Special Abilities if(HaveEnemy) { // dk: Make sure we wield weapons wielded if(!GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND))) OAI_EquipAppropriateWeapons(); DetermineCombatRound(oIntruder); } //:: NWN DETERMINE COMBAT ROUND /******************************************************************************/ /******************************************************************************/ //:: COMBAT IS OVER SO DO SOMETHING SMART //if we are in darkness, move out of the effect... if(GetHasEffect(EFFECT_TYPE_DARKNESS, oSelf)) { vector vWho = GetPosition(oSelf); vWho.x += IntToFloat(Random(3) * 10); vWho.y += IntToFloat(Random(3) * 10); ActionMoveToLocation(Location(oArea, vWho, 0.0), TRUE); return; } else if(GetHasEffect(EFFECT_TYPE_BLINDNESS, oSelf)) { switch(Random(8)) { case 0: PlayVoiceChat(VOICE_CHAT_HELP); break; case 1: PlayVoiceChat(VOICE_CHAT_NO); break; } ActionRandomWalk(); return; } int Ath = 1; object oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT); while(GetIsObjectValid(oAOE) && GetDistanceToObject(oAOE) < 10.0) { if(GetAreaOfEffectCreator(oAOE) != oSelf || !GetHasEffect(EFFECT_TYPE_AREA_OF_EFFECT, GetAreaOfEffectCreator(oAOE))) { ActionMoveAwayFromObject(oAOE, TRUE, 11.0); return; } Ath++; oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT, oSelf, Ath); } //Are We Shapeshifted if(!GetHasEffect(EFFECT_TYPE_POLYMORPH, OBJECT_SELF)) { OAI_RemoveEffects(OBJECT_SELF,OBJECT_SELF,EFFECT_TYPE_POLYMORPH); } //lastly, if we are not stupid, or certain races run to allies after battle. int nRacial = GetRacialType(oSelf); if(nRacial != RACIAL_TYPE_ANIMAL && nRacial != RACIAL_TYPE_VERMIN && nRacial != RACIAL_TYPE_MAGICAL_BEAST && nRacial != RACIAL_TYPE_DRAGON && nRacial != RACIAL_TYPE_CONSTRUCT && nRacial != RACIAL_TYPE_BEAST && nRacial != RACIAL_TYPE_ABERRATION && GetAbilityScore(oSelf, ABILITY_INTELLIGENCE) > 7) { object oFriend = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, oSelf, 1, CREATURE_TYPE_IS_ALIVE, TRUE); if(GetIsObjectValid(oFriend)) { if(GetDistanceToObject(oFriend) > 12.0) ActionMoveToObject(oFriend, TRUE, 6.0); else if(GetDistanceToObject(oFriend) < 4.0) ActionMoveAwayFromObject(oFriend, TRUE, 6.0); } if(GetSpawnInCondition(NW_FLAG_SEARCH)) ActionUseSkill(SKILL_SEARCH, OBJECT_SELF); if(GetSpawnInCondition(NW_FLAG_STEALTH)) ActionUseSkill(SKILL_HIDE, OBJECT_SELF); if(GetBattleCondition(OAI_RETURN_TO_SPAWNPOINT)) { ActionForceMoveToLocation(GetLocalLocation(OBJECT_SELF, "OAI_SPAWN_LOCATION")); } WalkWayPoints(); } //:: COMBAT IS OVER SO DO SOMETHING SMART /******************************************************************************/ } void HenchmenCombatRound(object oIntruder) { object oSelf = OBJECT_SELF; // * If someone has surrendered, then don't attack them. if (GetIsObjectValid(oIntruder) == TRUE) { if(GetIsEnemy(oIntruder) == FALSE) { ClearAllActions(TRUE); ActionAttack(OBJECT_INVALID); return; } } object oNearestTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); // **************************************** // SETUP AND SANITY CHECKS (Quick Returns) // **************************************** if(bkEvaluationSanityCheck(oIntruder, GetFollowDistance()) == TRUE) return; if(GetAssociateState(NW_ASC_IS_BUSY) || GetAssociateState(NW_ASC_MODE_DYING)) return; if(GetLocalInt(oSelf, "X2_HENCH_STEALTH_MODE")==2) SetLocalInt(oSelf, "X2_HENCH_STEALTH_MODE", 0); if(GetAssociateState(NW_ASC_MODE_STAND_GROUND) == TRUE && GetIsObjectValid(GetLastHostileActor()) == FALSE) return; if(BashDoorCheck(oIntruder)) return; // ** Store how difficult the combat is for this round int nDiff = GetCombatDifficulty(); SetLocalInt(oSelf, "NW_L_COMBATDIFF", nDiff); object oMaster = GetMaster(); int iHaveMaster = GetIsObjectValid(oMaster); // * Do henchmen specific things if I am a henchman otherwise run default AI if(iHaveMaster == TRUE) { // ******************************************* // Healing // ******************************************* // The FIRST PRIORITY: self-preservation // The SECOND PRIORITY: heal master; if(bkCombatAttemptHeal() == TRUE) return; // NEXT priority: follow or return to master for up to three rounds. if(bkCombatFollowMaster() == TRUE) return; //5. This check is to see if the master is being attacked and in need of help // * Guard Mode -- only attack if master attacking // * or being attacked. if(GetAssociateState(NW_ASC_MODE_DEFEND_MASTER)) { oIntruder = GetLastHostileActor(GetMaster()); if(!GetIsObjectValid(oIntruder)) { oIntruder = GetAttackTarget(GetMaster()); if(GetIsObjectValid(oIntruder) || GetLocalInt(oSelf, "X0_BATTLEJOINEDMASTER") == TRUE) { SetLocalInt(oSelf, "X0_BATTLEJOINEDMASTER", TRUE); if(GetIsObjectValid(oIntruder) == FALSE) { oIntruder = oNearestTarget; if(GetIsObjectValid(oIntruder) == FALSE) { //* turn off the "I am in battle" sub-mode SetLocalInt(oSelf, "X0_BATTLEJOINEDMASTER", FALSE); } } } // * Exit out and do nothing this combat round else { oIntruder = GetLastAttacker(oSelf); if(GetIsObjectValid(oIntruder) == FALSE) return; } } } if(GetAssociateType(oSelf) == ASSOCIATE_TYPE_HENCHMAN) { // 5% chance per round of speaking the relative challenge of the encounter. if(d100() > 95) { if(nDiff <= 1) VoiceLaugh(TRUE); // MODIFIED February 7 2003. This was confusing testing // else if (nDiff <= 4) VoiceThreaten(TRUE); // else VoiceBadIdea(); } } // I am a familiar FLEE if tough int iAmFamiliar = (GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oMaster) == oSelf); if(iAmFamiliar) { //Run away from tough enemies if(nDiff >= BK_FAMILIAR_COWARD || GetPercentageHPLoss(oSelf) < 40) { VoiceFlee(); ClearAllActions(); ActionMoveAwayFromObject(oNearestTarget, TRUE, 40.0); return; } } } if (GetIsObjectValid(oIntruder) || GetIsObjectValid(oNearestTarget)) { if(BreakIllusion(oIntruder)) return; if(DetermineAllyToRez()) return; /******************************************************************************/ //:: SPECIAL COMBAT ROLES //Dragons if((GetBattleCondition(OAI_ROLE_DRAGON) || GetRacialType(oSelf) == RACIAL_TYPE_DRAGON)) { if(SpecialDragonStuff(oIntruder)) return; } //Beholders if(GetBattleCondition(OAI_ROLE_BEHOLDER)) { SpecialBeholderStuff(oIntruder); return; } //Gelatinous Cubes if(GetBattleCondition(OAI_ROLE_GELATINOUSCUBE)) { SpecialGelatinousCubeStuff(oIntruder); return; } //Spiders if(GetBattleCondition(OAI_ROLE_SPIDER)) { //50% OR Heavily Wounded int nInjured = FloatToInt(IntToFloat(GetMaxHitPoints()) * 0.3); int nCHP = GetCurrentHitPoints(); if(Random(100) < 50 || nCHP < nInjured) { if(OAI_SpiderTactics(oIntruder)) return; } } //Mindflayers if(GetBattleCondition(OAI_ROLE_MINDFLAYER)) { if(OAI_MindFlayerCombat(oIntruder)) return; } //:: SPECIAL COMBAT ROLES /******************************************************************************/ /******************************************************************************/ //:: SPECIAL COMBAT TACTICS (FROM BIOWARE & OLANDER) //Special Ranged Tactics (Special Combat AI Run Through Ranged Combat) if(GetBattleCondition(OAI_COMBAT_FLAG_RANGED)) { RangedCombat(oIntruder); return; } //Special Defensive Tactics if(GetBattleCondition(OAI_COMBAT_FLAG_DEFENSIVE)) { DetermineCombatRound(oIntruder); return; } //Special Ambusher Tactics if(GetBattleCondition(OAI_COMBAT_FLAG_AMBUSHER)) { DetermineCombatRound(oIntruder); return; } //Special Cowardly Tactics if(GetBattleCondition(OAI_COMBAT_FLAG_COWARDLY)) { DetermineCombatRound(oIntruder); return; } //Shapechanger With Appearance Set in Spawn Script if(GetLocalInt(oSelf, "OAI_SHAPECHANGE") > 0) { if((Random(70) + 30) > ((GetCurrentHitPoints() * 100) / GetMaxHitPoints())) { // We won't polymorph if already so if(!GetHasEffect(EFFECT_TYPE_POLYMORPH, oSelf)) { effect eShape = SupernaturalEffect(EffectPolymorph(GetLocalInt(oSelf, "OAI_SHAPECHANGE") - 1)); effect eVis = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD); DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eShape, oSelf)); DelayCommand(1.0, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, GetLocation(oSelf))); } DeleteLocalInt(OBJECT_SELF, "OAI_SHAPECHANGE"); SetStatusCondition(OAI_I_CAN_POLYMORPH,FALSE); } } //Palemasters Always Try to Fight like a Palemaster if(GetStatusCondition(OAI_I_CAN_PM_MELEE)) { if(GetIsTargetInMeleeRange(oIntruder)) { MeleePalemaster(oIntruder); return; } } //Shapechanger With Feats if(GetStatusCondition(OAI_I_CAN_POLYMORPH)) { MeleeShifter(oIntruder); return; } //Summon Help First If Attacker is Close if(GetStatusCondition(OAI_I_CAN_SUMMON)) { if(TalentSummonAllies() == TRUE) { SetStatusCondition(OAI_I_CAN_SUMMON, FALSE); } } //:: SPECIAL COMBAT TACTICS (FROM BIOWARE) /******************************************************************************/ if(!GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND))) OAI_EquipAppropriateWeapons(); DetermineCombatRound(oIntruder); } } void bkRespondToHenchmenShout(object oShouter, int nShoutIndex, object oIntruder = OBJECT_INVALID, int nBanInventory=FALSE) { if(GetHasEffect(EFFECT_TYPE_PETRIFY, OBJECT_SELF) == TRUE) return; if(GetIsHenchmanDying() == TRUE) return; if(GetLocalInt(OBJECT_SELF,"Generic_Surrender")) return; object oLastObject; object oTrap; object oMaster; object oTarget; //ASSOCIATE SHOUT RESPONSES switch(nShoutIndex) { // * toggle search mode for henchmen case ASSOCIATE_COMMAND_TOGGLESEARCH: { if(GetActionMode(OBJECT_SELF, ACTION_MODE_DETECT) == TRUE) { SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, FALSE); } else { SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE); } break; } // * toggle stealth mode for henchmen case ASSOCIATE_COMMAND_TOGGLESTEALTH: { if(GetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH) == TRUE) { SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE); } else { SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE); } break; } // * June 2003: Stop spellcasting case ASSOCIATE_COMMAND_TOGGLECASTING: { if (GetLocalInt(OBJECT_SELF, "X2_L_STOPCASTING") == 10) { SetLocalInt(OBJECT_SELF, "X2_L_STOPCASTING", 0); VoiceCanDo(); } else if (GetLocalInt(OBJECT_SELF, "X2_L_STOPCASTING") == 0) { SetLocalInt(OBJECT_SELF, "X2_L_STOPCASTING", 10); VoiceCanDo(); } break; } case ASSOCIATE_COMMAND_INVENTORY: { if (nBanInventory == TRUE) { SpeakStringByStrRef(9066); } else { if(GetLocalInt(OBJECT_SELF, "X2_JUST_A_DISABLEEQUIP") == FALSE) { OpenInventory(OBJECT_SELF, oShouter); } else { SendMessageToPCByStrRef(GetMaster(), 100895); } } break; } case ASSOCIATE_COMMAND_PICKLOCK: bkManualPickNearestLock(); break; case ASSOCIATE_COMMAND_DISARMTRAP: bkAttemptToDisarmTrap(GetNearestTrapToObject(GetMaster()), TRUE); break; case ASSOCIATE_COMMAND_ATTACKNEAREST: ResetHenchmenState(); SetAssociateState(NW_ASC_MODE_DEFEND_MASTER, FALSE); SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE); DetermineCombatRound(); oTarget = GetAttackTarget(GetMaster()); if(GetIsObjectValid(oTarget) == TRUE) { if(GetObjectType(oTarget) == OBJECT_TYPE_PLACEABLE || GetObjectType(oTarget) == OBJECT_TYPE_DOOR) { ActionAttack(oTarget); } } break; case ASSOCIATE_COMMAND_FOLLOWMASTER: ResetHenchmenState(); SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE); DelayCommand(2.5, VoiceCanDo()); ActionForceFollowObject(GetMaster(), GetFollowDistance()); SetAssociateState(NW_ASC_IS_BUSY); DelayCommand(5.0, SetAssociateState(NW_ASC_IS_BUSY, FALSE)); break; case ASSOCIATE_COMMAND_GUARDMASTER: { ResetHenchmenState(); SetAssociateState(NW_ASC_MODE_DEFEND_MASTER); SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE); object oLastAttacker = GetLastHostileActor(GetMaster()); SetLocalInt(OBJECT_SELF, "X0_BATTLEJOINEDMASTER", TRUE); HenchmenCombatRound(oLastAttacker); break; } case ASSOCIATE_COMMAND_HEALMASTER: ResetHenchmenState(); if(TalentCureCondition()) { DelayCommand(2.0, VoiceCanDo()); return; } if(TalentHeal(TRUE, GetMaster())) { DelayCommand(2.0, VoiceCanDo()); return; } DelayCommand(2.5, VoiceCannotDo()); break; case ASSOCIATE_COMMAND_MASTERFAILEDLOCKPICK: if(!GetAssociateState(NW_ASC_MODE_STAND_GROUND) && GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS)) { oLastObject = GetLockedObject(GetMaster()); bkAttemptToOpenLock(oLastObject); } break; case ASSOCIATE_COMMAND_STANDGROUND: SetAssociateState(NW_ASC_MODE_STAND_GROUND); SetAssociateState(NW_ASC_MODE_DEFEND_MASTER, FALSE); DelayCommand(2.0, VoiceCanDo()); ActionAttack(OBJECT_INVALID); ClearAllActions(); break; // *********************************** // * AUTOMATIC SHOUTS - not player // * initiated // *********************************** case ASSOCIATE_COMMAND_MASTERSAWTRAP: if(!GetIsInCombat()) { if(!GetAssociateState(NW_ASC_MODE_STAND_GROUND)) { oTrap = GetLastTrapDetected(GetMaster()); bkAttemptToDisarmTrap(oTrap); } } break; case ASSOCIATE_COMMAND_MASTERUNDERATTACK: if(!GetIsInCombat(OBJECT_SELF)) HenchmenCombatRound(); break; case ASSOCIATE_COMMAND_MASTERATTACKEDOTHER: if(!GetAssociateState(NW_ASC_MODE_STAND_GROUND)) { if(!GetAssociateState(NW_ASC_MODE_DEFEND_MASTER)) { if(!GetIsInCombat(OBJECT_SELF)) { object oAttack = GetAttackTarget(GetMaster()); if(GetIsObjectValid(oAttack) && GetObjectSeen(oAttack, GetMaster())) { ClearAllActions(); HenchmenCombatRound(oAttack); } } } } break; case ASSOCIATE_COMMAND_MASTERGOINGTOBEATTACKED: if(!GetAssociateState(NW_ASC_MODE_STAND_GROUND)) { if(!GetIsInCombat(OBJECT_SELF)) { object oAttacker = GetGoingToBeAttackedBy(GetMaster()); if(GetIsObjectValid(oAttacker) && GetObjectSeen(oAttacker, GetMaster())) { ClearAllActions(); ActionMoveToObject(oAttacker, TRUE, 7.0); HenchmenCombatRound(oAttacker); } } } break; case ASSOCIATE_COMMAND_LEAVEPARTY: { oMaster = GetMaster(); string sTag = GetTag(GetArea(oMaster)); // * henchman cannot be kicked out in the reaper realm // * Followers can never be kicked out if(sTag == "GatesofCania" || GetIsFollower(OBJECT_SELF) == TRUE) return; if(GetIsObjectValid(oMaster)) { ClearAllActions(); if(GetAssociateType(OBJECT_SELF) == ASSOCIATE_TYPE_HENCHMAN) { FireHenchman(GetMaster(), OBJECT_SELF); } } break; } } } //:: HENCHMAN FUNCTIONS /******************************************************************************/ /******************************************************************************/ //:: SPECIAL NPC BEHAVIORS void OAI_GuardCheck(object oNPC) { object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF); object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oPC); //Ok to Wield Weapons //(This is a Waypoint that is placed in an Area) object oAllow = GetNearestObjectByTag("DisallowWeapons"); if(!GetIsObjectValid(oAllow)) return; //Is the Weapon a Weapon? if(IsWeapon(oItem) == FALSE) return; //Military Ranked Player object oRank = GetItemPossessedBy(oPC,RANKINSIGNIA); if(GetIsObjectValid(oRank)) return; if(oPC != OBJECT_INVALID && (GetDistanceBetween(OBJECT_SELF,oPC) < WARN_DISTANCE) && GetObjectSeen(oPC) && !GetIsEnemy(oPC)) { if(oItem != OBJECT_INVALID) { if(GetLocalObject(OBJECT_SELF,"LastOffender") == oPC) { if(GetLocalInt(OBJECT_SELF,"OffenseCount") == 2) { SpeakString(ATTACK_MSG); SetIsTemporaryEnemy(oPC,OBJECT_SELF,TRUE,ANGER_DUR); ActionAttack(oPC); } else if(GetLocalInt(OBJECT_SELF,"OffenseCount")==1) { ActionMoveToObject(oPC,TRUE); SetLocalInt(OBJECT_SELF,"OffenseCount",2); SpeakString(WARNING3); } else { SetLocalInt(OBJECT_SELF,"OffenseCount",1); SpeakString(WARNING2); } } else { SetLocalInt(OBJECT_SELF,"OffenseCount",0); SpeakString(WARNING1); SetLocalObject(OBJECT_SELF,"LastOffender",oPC); } } else { if( GetLocalObject(OBJECT_SELF,"LastOffender")!= OBJECT_INVALID) SpeakString(COMPLY_REPLY); DeleteLocalObject(OBJECT_SELF,"LastOffender"); SetLocalInt(OBJECT_SELF,"OffenseCount",0); } } } void OAI_Barmaid(object oNPC) { object oCustomer = GetLocalObject(OBJECT_SELF, "CUSTOMER"); int nRandom = d8(1); // Set this to the approx number of NPC's in the room object oBar = GetWaypointByTag("WP_BAR"); // Place this waypoint where she gets drinks from object oRest = GetWaypointByTag("WP_REST"); // Place this waypoint where she returns to rest object oCook = GetObjectByTag("COOK"); // Place an NPC Cook in the kitchen with this tag object oBartend = GetObjectByTag("BARTENDER");// Place an NPC Bartender at bar with this tag string sBarmaid = GetName(OBJECT_SELF); string sCook = GetName(oCook); string sBartend = GetName(oBartend); if(!GetIsObjectValid(oCustomer) && GetLocalInt(OBJECT_SELF, "BARMAID_STATE") < 1) { oCustomer = GetNearestCreature (CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, OBJECT_SELF, nRandom); if(oCustomer != oCook && oCustomer != oBartend && oCustomer != OBJECT_SELF && GetIsObjectValid(oCustomer)) { SetLocalInt (OBJECT_SELF, "BARMAID_STATE", 1); SetLocalObject (OBJECT_SELF, "CUSTOMER", oCustomer); ActionMoveToObject(oCustomer); switch(Random(4)) { case 0: ActionSpeakString ("Can I get you something?"); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_YES,oCustomer)); break; case 1: ActionSpeakString ("What would you like?"); break; case 2: ActionSpeakString ("How are we doing over here?"); break; case 3: ActionSpeakString ("Another round?"); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_GOODIDEA,oCustomer)); break; } ActionWait(5.0); ActionDoCommand (SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 2)); ActionMoveToObject(oBar); switch(Random(4)) { case 0: ActionSpeakString ("I need two ales and a bottle of mead."); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_CANDO,oCook)); break; case 1: ActionSpeakString ("Two whiskeys with a water back please."); break; case 2: ActionSpeakString ("They want a pitcher of ale and a loaf of bread"); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_CANTDO,oCook)); break; case 3: ActionSpeakString ("A bottle of wine and two glasses then."); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_TASKCOMPLETE,oCook)); break; } ActionWait(8.0); ActionDoCommand (SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 3)); ActionMoveToObject(oCustomer); switch(Random(4)) { case 0: ActionSpeakString ("Enjoy this friend."); break; case 1: ActionSpeakString ("That'll be 10 gold. I'm just kidding. 3 gold, please."); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_LAUGH,oCustomer)); break; case 2: ActionSpeakString ("You look like you could use this."); break; case 3: ActionSpeakString ("Ice brewed in the Frigid North, friend. Enjoy."); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_THANKS,oCustomer)); break; } ActionWait(3.0); ActionDoCommand (SetLocalObject(OBJECT_SELF, "CUSTOMER", OBJECT_INVALID)); ActionMoveToObject(oRest); //Enter the direction for her to face while on break or comment line below out! DelayCommand(4.0,AssignCommand(OBJECT_SELF,SetFacing(DIRECTION_WEST))); ActionWait(5.0); switch(Random(4)) { case 0: ActionSpeakString ("Slow night tonight, eh "+sBartend); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_YES,oBartend)); break; case 1: ActionSpeakString ("My feet are killing me."); break; case 2: ActionSpeakString ("Well they sure tip well here."); break; case 3: ActionSpeakString ("Look at that tender morsel!"); ActionDoCommand(PlayVoiceChat(VOICE_CHAT_LAUGH,oBartend)); break; } ActionWait(5.0); ActionDoCommand (SetLocalInt(OBJECT_SELF, "BARMAID_STATE", 0)); } } } void OAI_BarChatter(object oNPC) { object oSelf = OBJECT_SELF; object oFriend = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC); int iSelfState = GetLocalInt(oSelf,"CHAT_STATE"); int iLastRandom = GetLocalInt(oSelf,"CHAT_LINE"); int iChat = d10(); // Return if they are about to use the same lines over if (iChat == iLastRandom) return; // Don't talk if they are in convo already if (IsInConversation(oSelf) || IsInConversation(oFriend))return; // This section is the intro area of convo, use openers. if (iSelfState == 1) { switch(iChat) { case 0: SpeakString ("It's always good to see you friend."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("I enjoy getting to see you as well."))); break; case 1: SpeakString ("What's new with you these days?"); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Doing what I must and staying busy."))); break; case 2: SpeakString ("I hope your journey here was enjoyable."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("It was quite good thank you."))); break; case 3: SpeakString ("It has been awhile since we got together."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Seems like quite a time aye?"))); break; case 4: break; case 5: break; } } // Now say more direct lines if(iSelfState == 3) { switch(iChat) { case 0: AssignCommand(oFriend,SpeakString("Have you been busy of late?")); DelayCommand(3.0f,SpeakString ("Not anymore than usual.")); break; case 1: AssignCommand(oFriend,SpeakString ("I purchased a new mount the other day.")); DelayCommand(3.0f,SpeakString ("Indeed? That is rather exciting.")); break; case 2: AssignCommand(oFriend,SpeakString ("I am always glad to trade in this great township!")); DelayCommand(3.0f,SpeakString ("Aye, I am always impressed with the guard presence here.")); break; case 3: AssignCommand(oFriend,SpeakString ("Did you ever complete your studies?")); DelayCommand(3.0f,SpeakString ("Aye! Thank you for asking, about a month ago.")); break; case 4: break; case 5: AssignCommand(oFriend,SpeakString ("I think we should get something to eat, Aye?")); DelayCommand(3.0f,PlayVoiceChat(VOICE_CHAT_YES)); break; } } // Third part of conversation if(iSelfState == 5) { switch(iChat) { case 0: SpeakString ("I have been thinking about becoming more learned."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("That is interesting news, but sounds like a good idea."))); break; case 1: SpeakString ("Did you see the guild master when he visited the city?"); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Nay, but I heard he was about town this week."))); break; case 2: SpeakString ("I always enjoy seeing you here."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Aye, my feelings exactly friend."))); break; case 3: SpeakString ("We should do this more often."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Agreed, perhaps again next week aye?"))); break; case 4: break; case 5: SpeakString ("Do you think we should do this again?"); DelayCommand(3.0f,AssignCommand(oFriend,PlayVoiceChat(VOICE_CHAT_GOODIDEA))); break; } } // Fourth part of convo if (iSelfState == 7) { switch(iChat) { case 0: AssignCommand(oFriend,SpeakString ("It seems like we don't get much time to chat.")); DelayCommand(3.0f,SpeakString ("Aye, we should allow more time to get together, perhaps.")); break; case 1: AssignCommand(oFriend,SpeakString ("Look at that sturdy adventurer over there.")); DelayCommand(3.0f,SpeakString ("I noticed them when they came in, I don't know how they do it.")); break; case 2: AssignCommand(oFriend,SpeakString ("I should probably depart before too long.")); DelayCommand(3.0f,SpeakString ("Understood, I would happily spend hours chatting away with you.")); break; case 3: AssignCommand(oFriend,SpeakString ("Did you ever complete your studies?")); DelayCommand(3.0f,SpeakString ("Aye! Thank you for asking, about a month ago.")); break; case 4: break; case 5: SpeakString ("Those folks over there think we are quite odd!"); DelayCommand(3.0f,AssignCommand(oFriend,PlayVoiceChat(VOICE_CHAT_LAUGH))); break; } } // Fifth part of convo, this time have fun with PC's by using their names if(iSelfState == 9) { object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC); string sName = GetName(oPC); int iLevels = GetHitDice(oPC); switch(iChat) { case 0: SpeakString ("I need to make sure I save some gold to trade with."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Aye, it is easy to spend it all in this city."))); break; case 1: SpeakString ("Did you see the Lord Justicar yesterday?"); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Nay, but there's a man with alot on his shoulders."))); break; case 2: SpeakString ("I hear things are escalating towards conflict."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Grim news indeed, I hope all will pan out."))); break; case 3: SpeakString ("I think we should make a point of getting together more often."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Agreed, it is a good idea, perhaps again soon aye?"))); break; case 4: if(iLevels <= 10) { SpeakString ("I think that is "+sName+", isn't it? Becoming quite strong it appears."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Aye, I have watched them grow."))); } break; case 5: if(iLevels >= 15) { SpeakString ("I think that adventurer there is "+sName+"."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Aye, that is. They will achieve veteran status soon I think."))); } break; case 6: if(iLevels >= 20) { SpeakString ("Who is that? That is one well traveled adventurer from what I see."); DelayCommand(3.0f,AssignCommand(oFriend,SpeakString ("Indeed, that is "+sName+". I heard a guard mention that name the other day."))); } break; } } // Increment the state, save values and be prepped for next go around iSelfState++; if(iSelfState > 10) iSelfState = 0; SetLocalInt(oSelf,"CHAT_STATE",iSelfState); SetLocalInt(oSelf,"CHAT_LINE",iChat); } //Inn/Restaurant Cooks void OAI_Cooks(object oNPC) { int nCurrentJob = GetLocalInt(OBJECT_SELF,"INN_Cook_Job"); object oWaypoint; object oChair; if((GetLocalInt(OBJECT_SELF,"INN_Cook_Busy") > 1) && (GetLocalInt(OBJECT_SELF,"INN_Cook_Job") != 0)) { SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",0); } if(GetLocalInt(OBJECT_SELF,"INN_Cook_Busy") == 0) { switch (nCurrentJob) { case 1: break; case 2: break; case 3: SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",1); ClearAllActions(); ActionSpeakString("One " + GetLocalString(OBJECT_SELF,"INN_Cook_Job_Name") + " coming up."); ActionMoveToObject(GetWaypointByTag("INN_KitchenPrepTable")); ActionWait(2.0); ActionSpeakString("I need some flour...."); ActionMoveToObject(GetWaypointByTag("INN_KitchenItems3")); ActionWait(1.0); ActionMoveToObject(GetWaypointByTag("INN_KitchenPrepTable")); ActionWait(3.0); ActionSpeakString("and some eggs..."); ActionMoveToObject(GetWaypointByTag("INN_KitchenItems1")); ActionWait(1.0); ActionMoveToObject(GetWaypointByTag("INN_KitchenPrepTable")); ActionWait(2.0); ActionSpeakString("can't foget the main item..."); ActionMoveToObject(GetWaypointByTag("INN_KitchenItems2")); ActionWait(1.0); ActionMoveToObject(GetWaypointByTag("INN_KitchenPrepTable")); ActionWait(5.0); ActionSpeakString("now for the oven..."); ActionMoveToObject(GetWaypointByTag("INN_KitchenOven")); ActionWait(1.0); ActionMoveToObject(GetWaypointByTag("INN_KitchenPickup")); ActionSpeakString(GetLocalString(OBJECT_SELF,"INN_Cook_Job_Name") + " is done"); ActionDoCommand(SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",0)); ActionDoCommand(SetLocalInt(OBJECT_SELF,"INN_Cook_Job",0)); break; default: switch (d6()) { case 1: SpeakString("I hate to cleaning up this mess."); break; case 2: SpeakString("la de da, la de do..."); break; case 3: SpeakString("All " + GetName(GetObjectByTag("BARTENDER")) + " ever wants me to do is cook, cook, cook!"); break; default: break; } switch (d6()) { case 1: ClearAllActions(); SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",2); if (d4() == 1) { ActionSpeakString("Think I need to sit down for a bit."); } oChair = GetObjectByTag("INN_KitchenChair"); ActionSit(oChair); ActionWait(5+IntToFloat(d4())); ActionDoCommand(SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",0)); ClearAllActions(); break; case 2: ClearAllActions(); SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",2); oWaypoint= GetWaypointByTag("INN_KitchenItems1"); ActionMoveToObject(oWaypoint); ActionWait(IntToFloat(d4())); ActionDoCommand(SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",0)); break; case 3: ClearAllActions(); SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",2); oWaypoint= GetWaypointByTag("INN_KitchenItems2"); ActionMoveToObject(oWaypoint); ActionWait(IntToFloat(d4())); ActionDoCommand(SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",0)); break; case 4: ClearAllActions(); SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",2); oWaypoint= GetWaypointByTag("INN_KitchenItems3"); ActionMoveToObject(oWaypoint); ActionWait(IntToFloat(d4())); ActionDoCommand(SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",0)); break; case 5: ClearAllActions(); SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",2); oWaypoint= GetWaypointByTag("INN_KitchenOven"); ActionMoveToObject(oWaypoint); ActionWait(IntToFloat(d4())); ActionDoCommand(SetLocalInt(OBJECT_SELF,"INN_Cook_Busy",0)); break; } break; } } } void OAI_Stripper(object oNPC) { int nDance = GetLocalInt(oNPC,"STRIPPING"); if(nDance == 1) return; SetLocalInt(oNPC,"STRIPPING",1); //Wearing Now....Set Up in Toolset object oStrip05 = GetItemPossessedBy(oNPC,"strip05"); //Segment Clothing object oStrip01 = GetItemPossessedBy(oNPC,"strip01"); if(GetIsObjectValid(oStrip01) == FALSE) oStrip01 = CreateItemOnObject("strip01",oNPC,1); object oStrip02 = GetItemPossessedBy(oNPC,"strip02"); if(GetIsObjectValid(oStrip02) == FALSE) oStrip02 = CreateItemOnObject("strip02",oNPC,1); object oStrip03 = GetItemPossessedBy(oNPC,"strip03"); if(GetIsObjectValid(oStrip03) == FALSE) oStrip03 = CreateItemOnObject("strip03",oNPC,1); object oStrip04 = GetItemPossessedBy(oNPC,"strip04"); if(GetIsObjectValid(oStrip04) == FALSE) oStrip04 = CreateItemOnObject("strip04",oNPC,1); //the tease part ActionPlayAnimation(ANIMATION_FIREFORGET_TAUNT,1.0); ActionPlayAnimation(ANIMATION_FIREFORGET_BOW,1.0); //this removes dress and reequips next item to give //appearance of taking off only the dress ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_CHEST)); ActionEquipItem(oStrip04, INVENTORY_SLOT_CHEST); //continue dancing ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY2,1.0); ActionPlayAnimation(ANIMATION_FIREFORGET_BOW,1.0); ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW,1.0); ActionPlayAnimation(ANIMATION_LOOPING_WORSHIP,1.0); //this removes current clothing and equips //next clothing item to give appearance of only taking off //one item of clothing ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_CHEST)); ActionEquipItem(oStrip03, INVENTORY_SLOT_CHEST); //continue dancing ActionPlayAnimation(ANIMATION_LOOPING_WORSHIP,1.0); ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW,1.0); ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY2,1.0); ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0); ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW,1.0); //remove current clothes and equip next item of clothing //gives appearance of removing only one item of clothing ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_CHEST)); ActionEquipItem(oStrip02, INVENTORY_SLOT_CHEST); //continue dancing ActionPlayAnimation(ANIMATION_LOOPING_TALK_FORCEFUL,1.0); ActionPlayAnimation(ANIMATION_FIREFORGET_BOW,1.0); ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW,1.0); ActionPlayAnimation(ANIMATION_LOOPING_TALK_PLEADING,1.0); ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW,1.0); //remove clothing and equip next clothing item ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_CHEST)); ActionEquipItem(oStrip01, INVENTORY_SLOT_CHEST); //more dancing ActionPlayAnimation(ANIMATION_FIREFORGET_TAUNT,1.0); ActionPlayAnimation(ANIMATION_LOOPING_WORSHIP,1.0); ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY2,1.0); ActionPlayAnimation(ANIMATION_FIREFORGET_BOW,1.0); //put clothes back on ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_CHEST)); ActionEquipItem(oStrip05, INVENTORY_SLOT_CHEST); ActionDoCommand(SetLocalInt(oNPC,"STRIPPING",0)); } //From Tony K's Henchman AI void OAI_DetermineSpecialBehavior(object oTarget = OBJECT_INVALID) { 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(oTarget) && !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(); DetermineCombatRound(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(); DetermineCombatRound(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(); DetermineCombatRound(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(); DetermineCombatRound(oTarget); } } // Herbivore behavior routine else if(GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE)) { // no current attacker & not currently in combat if(!GetIsObjectValid(oTarget) && (GetIsInCombat() == FALSE)) { if(!GetIsObjectValid(GetAttemptedAttackTarget()) && !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); } } } int DetermineChanceOfAttack(object oPerceived) { float fCR = GetChallengeRating(OBJECT_SELF); int iPartyMembers = 1; if(fCR < 1.0) fCR = 1.0; fCR = fCR + 1.0; float fPartyModFactor; float fModFactor = sqrt((fCR - (GetHitDice(oPerceived) * 0.5)) / (GetHitDice(oPerceived) * 0.5)); if(fModFactor > 1.0) fModFactor = 1.0; if(fModFactor < 0.0) fModFactor = 0.0; object oPartyMember = GetFirstFactionMember(oPerceived, TRUE); while(GetIsObjectValid(oPartyMember)) { if(oPartyMember != oPerceived && GetDistanceBetween(oPerceived, oPartyMember) < 15.0) { fPartyModFactor = sqrt((fCR - (GetHitDice(oPartyMember) * 0.5)) / (GetHitDice(oPartyMember) * 0.5)); if(fPartyModFactor > 1.0) fPartyModFactor = 1.0; if(fPartyModFactor < 0.0) fPartyModFactor = 0.0; fModFactor = fModFactor * fPartyModFactor; iPartyMembers++; } oPartyMember = GetNextFactionMember(oPerceived); } float fChance = 100.0 * fModFactor; if(fChance == 0.0) fChance = 5.0 / IntToFloat(iPartyMembers); int iChance = FloatToInt(fChance); if(iChance == 0) iChance = 1; return iChance; } float GetEnemyCR (object oEnemy, float fEnemyCR) { if(GetIsPC(oEnemy)) return IntToFloat(GetHitDice(oEnemy)); else return GetChallengeRating(oEnemy); } float GetTheirCR(object oEnemy, float f_TheirCR, float fRange, object oTheirs) { oTheirs = GetFirstObjectInShape(SHAPE_SPHERE,fRange,GetLocation(OBJECT_SELF)); while(GetIsObjectValid(oTheirs)) { if(GetObjectType(oTheirs) == OBJECT_TYPE_CREATURE && GetIsEnemy(oTheirs,OBJECT_SELF)) { if(GetIsPC(oTheirs)) f_TheirCR = f_TheirCR + IntToFloat(GetHitDice(oTheirs)); else f_TheirCR = f_TheirCR + GetChallengeRating(oTheirs); } oTheirs = GetNextObjectInShape(SHAPE_SPHERE,fRange,GetLocation(OBJECT_SELF)); } return f_TheirCR; } float GetOurCR(float f_OurCR, float fRange, object oMine) { f_OurCR = GetChallengeRating(OBJECT_SELF); oMine = GetFirstObjectInShape(SHAPE_SPHERE,fRange,GetLocation(OBJECT_SELF)); while (GetIsObjectValid(oMine)) { if(GetObjectType(oMine) == OBJECT_TYPE_CREATURE && GetIsFriend(oMine,OBJECT_SELF) && GetLocalInt(oMine,"RETREATED") != 1 && GetLocalInt(OBJECT_SELF,"FOF_DEAD") != 1) { f_OurCR = f_OurCR + GetChallengeRating(oMine); } oMine = GetNextObjectInShape(SHAPE_SPHERE,fRange,GetLocation(OBJECT_SELF)); } return f_OurCR; } void FOF_Set(object oCreature = OBJECT_SELF) { //basic restrictions: caller is not undead, construct, or HD > 5 if(GetRacialType(OBJECT_SELF) != RACIAL_TYPE_UNDEAD && GetRacialType(OBJECT_SELF) != RACIAL_TYPE_CONSTRUCT && (GetHitDice(OBJECT_SELF) < 6 || GetChallengeRating(OBJECT_SELF) < 6.0)) //fairly arbitrary, but we can work with it { //Feat-based restrictions: caller has no special fear immunity if(!GetHasFeat(FEAT_AURA_OF_COURAGE,OBJECT_SELF) && !GetHasFeat(FEAT_FEARLESS,OBJECT_SELF) && !GetHasFeat(FEAT_RESIST_NATURES_LURE)) { //Is it too stupid to know better? Caller has basic intelligence. if(GetAbilityScore(OBJECT_SELF,ABILITY_INTELLIGENCE) > 5) { SetLocalInt(OBJECT_SELF,"FIGHT_OR_FLIGHT",1); SetListening(OBJECT_SELF,TRUE); SetListenPattern(OBJECT_SELF,"RETREAT_CHECK",5000); SetListenPattern(OBJECT_SELF,"GUARD_ME",5001); //FOF 011203: Set Green/Seasoned/Veteran by HD //Veterans = HD 5 if(GetHitDice(OBJECT_SELF) > 4) { SetLocalFloat(OBJECT_SELF,"fRaw",5.0); } //Seasoned = HD 3-4 if(GetHitDice(OBJECT_SELF) == 3 || GetHitDice(OBJECT_SELF) == 4) { SetLocalFloat(OBJECT_SELF,"fRaw",4.0); } //Green = HD 1-2 if(GetHitDice(OBJECT_SELF) == 1 || GetHitDice(OBJECT_SELF) == 2) { SetLocalFloat(OBJECT_SELF,"fRaw",3.0); } } } } } void FOF_Execute(object oCreature = OBJECT_SELF) { object oEnemy; object oMaster; int nDom; float f_SelfCR = GetChallengeRating(OBJECT_SELF); float fRange = 20.0; float fEnemyCR; float fCompare; float fHP = IntToFloat(GetCurrentHitPoints(OBJECT_SELF)); float fMax = IntToFloat(GetMaxHitPoints(OBJECT_SELF)); int nFloor; float fRaw = GetLocalFloat(OBJECT_SELF,"fRaw"); float f_TheirCR; float f_OurCR; object oTheirs; object oMine; float fGroupDiff; float fEnemyHP; float fEnemyMax; float fEnemyPercentage; float fMyPercentage; int nSave; float fBase = 20.0; //Determine oEnemy if(GetLastHostileActor() != OBJECT_INVALID) { oEnemy = GetLastHostileActor(); } else { oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY); } //A quick check to see if oEnemy is dominated oMaster = GetMaster(oEnemy); if(GetIsObjectValid(oMaster)) { if(GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_DOMINATED,oMaster))) { if(GetAssociate(ASSOCIATE_TYPE_DOMINATED,oMaster) == oEnemy) { nDom = 1; } else { nDom = 0; } } } //Gets the individual enemy's CR or HD, as appropriate fEnemyCR = GetEnemyCR(oEnemy,fEnemyCR); //Compares caller's to enemy's CR. fCompare will impact the //key denominator later. fCompare = f_SelfCR / (fEnemyCR + 0.01); //Get the enemy group's total CR or HD, as appropriate f_TheirCR = GetTheirCR(oEnemy,f_TheirCR,fRange,oTheirs); //Get the caller's group's total CR or HD, as appropriate. f_OurCR = GetOurCR(f_OurCR,fRange,oMine); //Compares caller group's to enemy group's CR. fGroupDiff //will impact the key denominator later. fGroupDiff = f_OurCR / (f_TheirCR + 0.01); //HERE IS THE IMPORTANT LINE: //At a base level of 4.0 for fRaw, in combats where the //opponents and groups are evenly matched, the caller will //flee when HP reaches 1/6 of max. nFloor = FloatToInt(fMax / (fRaw * (fCompare + fGroupDiff + 0.01))); //Use the same factors to modify the base Fear save nSave = FloatToInt(fBase / (fCompare + fGroupDiff + 0.01)); //How bad off is the enemy? fEnemyHP = IntToFloat(GetCurrentHitPoints(oEnemy)); fEnemyMax = IntToFloat(GetMaxHitPoints(oEnemy)); fEnemyPercentage = fEnemyHP / (fEnemyMax + 0.01); //How bad off am I? fMyPercentage = fHP / (fMax + 0.01); //A bit of a fig leaf here to allow creatures who heard //the RETREAT_CHECK shout to run even if they are in //better HP shape than oEnemy. int nHPCheck; if(fMyPercentage < fEnemyPercentage || GetLocalInt(OBJECT_SELF,"HEARD_THE_CALL") == 1) { nHPCheck = 1; } //Issues the command to do a Will save vs Fear if caller's //HP falls below the floor AND caller is in worse HP shape //than its opponent (to give the monsters SOME incentive to //slug it out). if(GetCurrentHitPoints(OBJECT_SELF) <= nFloor && nHPCheck == 1) { //A little bit to exclude dominated creatures. if (nDom == 0) { //Performs a will save modified by the factors. //Current base of 20 is deceptive; for equal //individual/group strengths, the save is 10, or //20 / (1 + 1) if(WillSave(OBJECT_SELF,nSave,SAVING_THROW_TYPE_FEAR,oEnemy) == 0) { SignalFlee(oEnemy,fRange); DelayCommand(1.0, ActionDoCommand(DetermineCombatRound())); } else { //If you've made your will save and you're //a leader, you still summon allies to help you. if(GetLocalInt(OBJECT_SELF,"OAI_I_AM_A_LEADER") == 1) CallForHelp(); else DelayCommand(1.0, ActionDoCommand(DetermineCombatRound())); } } } //Reset HEARD_THE_CALL to zero in the event this script //was called by a RETREAT_CHECK shout. SetLocalInt(OBJECT_SELF,"HEARD_THE_CALL",0); } void CallForHelp() { SpeakString("GUARD_ME",TALKVOLUME_SILENT_TALK); DelayCommand(1.0, ActionDoCommand(DetermineCombatRound())); } void SignalFlee(object oEnemy, float fRange) { object oTest; //Is there a leader within fRange + 20.0? oTest = GetFirstObjectInShape(SHAPE_SPHERE,fRange + 20.0,GetLocation(OBJECT_SELF),OBJECT_TYPE_CREATURE); while(GetIsObjectValid(oTest)) { //Leader...Break Out of Loop if(GetLocalInt(oTest,"OAI_I_AM_A_LEADER") == 1) break; oTest = GetNextObjectInShape(SHAPE_SPHERE,fRange + 20.0,GetLocation(OBJECT_SELF),OBJECT_TYPE_CREATURE); } //if we found no leader (the oTest is still a invalid object) if(GetIsObjectValid(oTest) == FALSE) { oTest = GetNearestObjectByTag("OAI_SAFE", OBJECT_SELF); } //Retreat to leader or rallying point, or scatter. if(GetLocalInt(oTest,"OAI_I_AM_A_LEADER") == 1 && GetDistanceToObject(oTest) >= 10.0) { ClearAllActions(); SpeakString("I NEED SOME HELP HERE!"); PlayVoiceChat(VOICE_CHAT_HELP); SetLocalInt(OBJECT_SELF,"RETREATED",1); ActionMoveToObject(oTest,TRUE,2.0); ActionDoCommand(SetLocalInt(OBJECT_SELF,"RETREATED",0)); ActionDoCommand(SetCommandable(TRUE,OBJECT_SELF)); SetCommandable(FALSE,OBJECT_SELF); } else if(GetIsObjectValid(oTest) == TRUE && GetDistanceToObject(oTest) >= 10.0) { ClearAllActions(); SpeakString("Fall back and regroup!"); PlayVoiceChat(VOICE_CHAT_GUARDME); SetLocalInt(OBJECT_SELF,"RETREATED",1); ActionMoveToObject(oTest,TRUE,2.0); ActionDoCommand(SetLocalInt(OBJECT_SELF,"RETREATED",0)); ActionDoCommand(SetCommandable(TRUE,OBJECT_SELF)); SetCommandable(FALSE,OBJECT_SELF); } else { ClearAllActions(); SpeakString("RUN! I AM OUT OF HERE!"); PlayVoiceChat(VOICE_CHAT_FLEE); SetLocalInt(OBJECT_SELF,"RETREATED",1); ActionMoveAwayFromObject(oEnemy,TRUE,fRange + 10.0); ActionDoCommand(SetLocalInt(OBJECT_SELF,"RETREATED",0)); ActionDoCommand(DestroyObject(OBJECT_SELF)); SetCommandable(FALSE,OBJECT_SELF); } //Shout a RETREAT_CHECK, but not if you're the one making the //check based on an ally's call. Reads an int set in nw_c2_default4. if(GetLocalInt(OBJECT_SELF,"HEARD_THE_CALL") == 1) { SetLocalInt(OBJECT_SELF,"HEARD_THE_CALL",0); } else { SpeakString("RETREAT_CHECK",TALKVOLUME_SILENT_TALK); } } //void main() {}