diff --git a/_content/Compiler - Top Hak.bat b/_content/Compiler - Top Hak.bat new file mode 100644 index 0000000..bf25c20 --- /dev/null +++ b/_content/Compiler - Top Hak.bat @@ -0,0 +1,4 @@ +:loop +"C:\NWN Work\nwnsc.exe" -o -w -n "C:\Games\Steam\steamapps\common\Neverwinter Nights" -i "D:\NWN Repos\Rune_PRC8\_content\_hak\rune_prc8_top";"D:\NWN Repos\PRC8\nwn\nwnprc\trunk\include" "D:\NWN Repos\Rune_PRC8\_content\_hak\rune_prc8_top\*.nss" +if %errorLevel% == -1 goto :loop +pause \ No newline at end of file diff --git a/_content/_hak/rune_prc8_top/dmfi_dmw_inc.nss b/_content/_hak/rune_prc8_top/dmfi_dmw_inc.nss new file mode 100644 index 0000000..8ce182f --- /dev/null +++ b/_content/_hak/rune_prc8_top/dmfi_dmw_inc.nss @@ -0,0 +1,611 @@ +// VOICE CONFIGURATION - NEW IN 1.07 and UP + +// Set this to 0 if you want to DISABLE listening by NPCs for performance reasons. +// See readme for additional information regarding possible issues and effects. +const int DMFI_LISTENING_GLOBAL = 1; + + +// NOTE: OMW_COLORS is an invisible object that must be present in your module. +// It has high ascii characters in the name and is used to get the color codes. +// This was ripped wholeheartedly by an example posted by Richterm on the bioboards. + +string DST_COLOR_TAGS = GetName(GetObjectByTag("dem_color_text")); +string DST_COLOR_WHITE = GetSubString(DST_COLOR_TAGS, 0, 6); +string DST_COLOR_YELLOW = GetSubString(DST_COLOR_TAGS, 6, 6); +string DST_COLOR_MAGENTA = GetSubString(DST_COLOR_TAGS, 12, 6); +string DST_COLOR_CYAN = GetSubString(DST_COLOR_TAGS, 18, 6); +string DST_COLOR_RED = GetSubString(DST_COLOR_TAGS, 24, 6); +string DST_COLOR_GREEN = GetSubString(DST_COLOR_TAGS, 30, 6); +string DST_COLOR_BLUE = GetSubString(DST_COLOR_TAGS, 36, 6); + +// Colors for each type of roll. Change the colors if you like. +string DMFI_ROLL_COLOR = DST_COLOR_CYAN; +string DST_COLOR_NORMAL = DST_COLOR_WHITE; + +int DMW_START_CUSTOM_TOKEN = 8000; + +//Retrieve targetting information +object oMySpeaker = GetLastSpeaker(); +object oMyTarget = GetLocalObject(oMySpeaker, "dmfi_univ_target"); +location lMyLoc = GetLocalLocation(oMySpeaker, "dmfi_univ_location"); + +// checks if a nearby object is destroyable +int dmwand_isnearbydestroyable(); +// Check if the target can be created with CreateObject +int dmwand_istargetcreateable(); +//Check if target is a destroyable object +int dmwand_istargetdestroyable(); +// checks if the wand was NOT clicked on an object +int dmwand_istargetinvalid(); +// check if the target has an inventory +int dmwand_istargetinventory(); +//Check if the target is not the wand's user +int dmwand_istargetnotme(); +//Check if target is an NPC or monster +int dmwand_istargetnpc(); +//Check if the target is a PC +int dmwand_istargetpc(); +//Check if the target is a PC and not me +int dmwand_istargetpcnme(); +// Check if the target is a PC or NPC +// uses the CON score currently +int dmwand_istargetpcornpc(); +//Check if the target is a PC or an npc and not me +int dmwand_istargetpcornpcnme(); +// Check if target is a placeable +int dmwand_istargetplaceable(); +//bulds the conversion +int dmwand_BuildConversationDialog(int nCurrent, int nChoice, string sConversation, string sParams); +int dmw_conv_ListPlayers(int nCurrent, int nChoice, string sParams = ""); +int dmw_conv_Start(int nCurrent, int nChoice, string sParams = ""); +void dmwand_BuildConversation(string sConversation, string sParams); +void dmwand_StartConversation(); + +// DMFI Color Text function. It returns a colored string. +// sText is the string that will be colored and sColor is the color +// options: yellow, magenta, cyan, red, green, blue - truncated at first letter +// Ex: sMsg = ColorText(sMsg, "y"); //Add the include file - yields yellow colored msg. +string ColorText(string sText, string sColor); +string ColorText(string sText, string sColor) +{ + string sApply = DST_COLOR_NORMAL; + string sTest = GetStringLowerCase(GetStringLeft(sColor, 1)); + if (sTest=="y") sApply = DST_COLOR_YELLOW; + else if (sTest == "m") sApply = DST_COLOR_MAGENTA; + else if (sTest == "c") sApply = DST_COLOR_CYAN; + else if (sTest == "r") sApply = DST_COLOR_RED; + else if (sTest == "g") sApply = DST_COLOR_GREEN; + else if (sTest == "r") sApply = DST_COLOR_BLUE; + + string sFinal = sApply + sText + DST_COLOR_NORMAL; + return sFinal; +} + + +int dmwand_isnearbydestroyable() +{ + object oMyTest = GetFirstObjectInShape(SHAPE_CUBE, 0.6, lMyLoc, FALSE, OBJECT_TYPE_ALL); + int nTargetType = GetObjectType(oMyTest); + return (GetIsObjectValid(oMyTest) && (! GetIsPC(oMyTest)) && ((nTargetType == OBJECT_TYPE_ITEM) || (nTargetType == OBJECT_TYPE_PLACEABLE) || (nTargetType == OBJECT_TYPE_CREATURE))); +} + +int dmwand_istargetcreateable() +{ + if(! GetIsObjectValid(oMyTarget)) { return FALSE; } + + int nTargetType = GetObjectType(oMyTarget); + return ((nTargetType == OBJECT_TYPE_ITEM) || (nTargetType == OBJECT_TYPE_PLACEABLE) || (nTargetType == OBJECT_TYPE_CREATURE)); +} + +int dmwand_istargetdestroyable() +{ + if(! GetIsObjectValid(oMyTarget)) { return FALSE; } + + int nTargetType = GetObjectType(oMyTarget); + if(! GetIsPC(oMyTarget)) + { + return ((nTargetType == OBJECT_TYPE_ITEM) || (nTargetType == OBJECT_TYPE_PLACEABLE) || (nTargetType == OBJECT_TYPE_CREATURE)); + } + return FALSE; +} + +int dmwand_istargetinvalid() +{ + return !GetIsObjectValid(oMyTarget); +} + +int dmwand_istargetinventory() +{ + return (GetIsObjectValid(oMyTarget) && GetHasInventory(oMyTarget)); +} + +int dmwand_istargetnotme() +{ + return (GetIsObjectValid(oMyTarget) && (oMySpeaker != oMyTarget)); +} + +int dmwand_istargetpc() +{ + return (GetIsObjectValid(oMyTarget) && GetIsPC(oMyTarget)); +} + +int dmwand_istargetpcnme() +{ + return (GetIsObjectValid(oMyTarget) && GetIsPC(oMyTarget) && (oMySpeaker != oMyTarget)); +} + +int dmwand_istargetpcornpc() +{ + return (GetIsObjectValid(oMyTarget) && GetAbilityScore(oMyTarget, ABILITY_CONSTITUTION)); +} + +int dmwand_istargetnpc() +{ + return (dmwand_istargetpcornpc() && (!GetIsPC(oMyTarget))); +} + +int dmwand_istargetpcornpcnme() +{ + return (dmwand_istargetpcornpc() && (oMySpeaker != oMyTarget)); +} + +int dmwand_istargetplaceable() +{ + if(! GetIsObjectValid(oMyTarget)) { return FALSE; } + + int nTargetType = GetObjectType(oMyTarget); + return (nTargetType == OBJECT_TYPE_PLACEABLE); +} + +int dmw_conv_Start(int nCurrent, int nChoice, string sParams = "") +{ + string sText = ""; + string sCall = ""; + string sCallParams = ""; + + switch(nCurrent) + { + case 0: + nCurrent = 0; + sText = "Hello there, DM. What can I do for you?"; + sCall = ""; + sCallParams = ""; + break; + + case 1: + nCurrent = 1; + if(dmwand_istargetpcnme()) + { + sText = "Penguin this player."; + sCall = "func_Toad"; + sCallParams = ""; + break; + } + case 2: + nCurrent = 2; + if(dmwand_istargetpcnme()) + { + sText = "Unpenguin this player."; + sCall = "func_Untoad"; + sCallParams = ""; + break; + } + case 3: + nCurrent = 3; + if(dmwand_istargetpcnme()) + { + sText = "Boot this player."; + sCall = "func_KickPC"; + sCallParams = ""; + break; + } + case 4: + nCurrent = 4; + if(dmwand_istargetinvalid()) + { + sText = "List all players..."; + sCall = "conv_ListPlayers"; + sCallParams = "func_PlayerListConv"; + break; + } + + case 5: + nCurrent = 5; + if(dmwand_istargetpcnme()) + { + sText = "Jump this player to my location."; + sCall = "func_JumpPlayerHere"; + sCallParams = ""; + break; + } + case 6: + nCurrent = 6; + if(dmwand_istargetpcnme()) + { + sText = "Jump me to this player's location."; + sCall = "func_JumpToPlayer"; + sCallParams = ""; + break; + } + case 7: + nCurrent = 7; + if(dmwand_istargetpcnme()) + { + sText = "Jump this player's party to my location."; + sCall = "func_JumpPartyHere"; + sCallParams = ""; + break; + } + default: + nCurrent = 0; + sText = ""; + sCall = ""; + sCallParams = ""; + break; + } + + SetLocalString(oMySpeaker, "dmw_dialog" + IntToString(nChoice), sText); + SetLocalString(oMySpeaker, "dmw_function" + IntToString(nChoice), sCall); + SetLocalString(oMySpeaker, "dmw_params" + IntToString(nChoice), sCallParams); + + return nCurrent; +} + +void DMFI_untoad(object oTarget, object oUser) +{ +if (GetLocalInt(oTarget, "toaded")==1) + { + effect eMyEffect = GetFirstEffect(oTarget); + while(GetIsEffectValid(eMyEffect)) + { + if(GetEffectType(eMyEffect) == EFFECT_TYPE_POLYMORPH || + GetEffectType(eMyEffect) == EFFECT_TYPE_PARALYZE) + RemoveEffect(oTarget, eMyEffect); + + eMyEffect = GetNextEffect(oTarget); + } + } +else + { + FloatingTextStringOnCreature("Dude, he is no toad!", oUser); + } +} + +void DMFI_toad(object oTarget, object oUser) +{ + effect ePenguin = EffectPolymorph(POLYMORPH_TYPE_PENGUIN); + effect eParalyze = EffectParalyze(); + SendMessageToPC(oUser, "Penguin? Don't you mean toad?"); + AssignCommand(oTarget, ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePenguin, oTarget)); + AssignCommand(oTarget, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eParalyze, oTarget)); + SetLocalInt(oTarget, "toaded", 1); +} + +void dmwand_KickPC(object oTarget, object oUser) +{ + // Create a lightning strike, thunder, scorch mark, and random small + // lightnings at target's location + location lMyLoc = GetLocation (oTarget); + AssignCommand( oUser, ApplyEffectAtLocation ( DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_LIGHTNING_M), lMyLoc)); + AssignCommand ( oUser, PlaySound ("as_wt_thundercl3")); + object oScorch = CreateObject ( OBJECT_TYPE_PLACEABLE, "plc_weathmark", lMyLoc, FALSE); + object oTargetArea = GetArea(oUser); + int nXPos, nYPos, nCount; + for(nCount = 0; nCount < 5; nCount++) + { + nXPos = Random(10) - 5; + nYPos = Random(10) - 5; + + vector vNewVector = GetPositionFromLocation(lMyLoc); + vNewVector.x += nXPos; + vNewVector.y += nYPos; + + location lNewLoc = Location(oTargetArea, vNewVector, 0.0); + AssignCommand( oUser, ApplyEffectAtLocation ( DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_LIGHTNING_S), lNewLoc)); + } + DelayCommand ( 20.0, DestroyObject ( oScorch)); + + SendMessageToAllDMs (GetName(oTarget) + " was booted from the game. PC CD KEY: " + GetPCPublicCDKey(oTarget) + " PC IP ADDRESS: " + GetPCIPAddress(oTarget)); + PrintString(GetName(oTarget) + " was booted from the game. PC CD KEY: " + GetPCPublicCDKey(oTarget) + " PC IP ADDRESS: " + GetPCIPAddress(oTarget)); + + // Kick the target out of the game + BootPC(oTarget); +} + +void dmwand_JumpPlayerHere() +{ + location lJumpLoc = GetLocation(oMySpeaker); + AssignCommand(oMyTarget, ClearAllActions()); + AssignCommand(oMyTarget, ActionJumpToLocation(lJumpLoc)); +} + +//Added by hahnsoo, jumps a party to the DM +void dmwand_JumpPartyHere() +{ + location lJumpLoc = GetLocation(oMySpeaker); + object oParty = GetFirstFactionMember(oMyTarget); + while (GetIsObjectValid(oParty)) + { + AssignCommand(oParty, ClearAllActions()); + AssignCommand(oParty, ActionJumpToLocation(lJumpLoc)); + oParty = GetNextFactionMember(oMyTarget); + } +} + +void dmwand_JumpToPlayer() +{ + location lJumpLoc = GetLocation(oMyTarget); + AssignCommand(oMySpeaker, ActionJumpToLocation(lJumpLoc)); +} + +void dmwand_PlayerListConv(string sParams) +{ + int nPlayer = StringToInt(sParams); + int nCache; + int nCount; + + object oPlayer = GetLocalObject(oMySpeaker, "dmw_playercache" + IntToString(nPlayer)); + oMyTarget = oPlayer; + SetLocalObject(oMySpeaker, "dmfi_univ_target", oMyTarget); + + //Go back to the first conversation level + dmwand_BuildConversation("Start", ""); +} + +//:://///////////////////////////////////////////// +//:: File: dmw_conv_inc +//:: +//:: Conversation functions for the DM's Helper +//::////////////////////////////////////////////// + +int dmwand_BuildConversationDialog(int nCurrent, int nChoice, string sConversation, string sParams) +{ + + if(TestStringAgainstPattern(sConversation, "ListPlayers")) + { + return dmw_conv_ListPlayers(nCurrent, nChoice, sParams); + } + + if(TestStringAgainstPattern(sConversation, "Start")) + { + return dmw_conv_Start(nCurrent, nChoice, sParams); + } + + return FALSE; +} + +void dmwand_BuildConversation(string sConversation, string sParams) +{ + int nLast; + int nTemp; + int nChoice = 1; + int nCurrent = 1; + int nMatch; + + if(TestStringAgainstPattern(sParams, "prev")) + { + //Get the number choice to start with + nCurrent = GetLocalInt(oMySpeaker, "dmw_dialogprev"); + + //Since we're going to the previous page, there will be a next + SetLocalString(oMySpeaker, "dmw_dialog9", "Next ->"); + SetLocalString(oMySpeaker, "dmw_function9", "conv_" + sConversation); + SetLocalString(oMySpeaker, "dmw_params9", "next"); + SetLocalInt(oMySpeaker, "dmw_dialognext", nCurrent); + + nChoice = 8; + for(;nChoice >= 0; nChoice--) + { + int nTemp1 = nCurrent; + int nTemp2 = nCurrent; + nMatch = nTemp2; + while((nCurrent == nMatch) && (nTemp2 > 0)) + { + nTemp2--; + nMatch = dmwand_BuildConversationDialog(nTemp2, nChoice, sConversation, sParams); + } + + if(nTemp2 <= 0) + { + //we went back too far for some reason, so make this choice blank + SetLocalString(oMySpeaker, "dmw_dialog" + IntToString(nChoice), ""); + SetLocalString(oMySpeaker, "dmw_function" + IntToString(nChoice), ""); + SetLocalString(oMySpeaker, "dmw_params" + IntToString(nChoice), ""); + } + nLast = nTemp; + nTemp = nTemp1; + nTemp1 = nMatch; + nCurrent = nMatch; + } + + if(nMatch > 0) + { + SetLocalString(oMySpeaker, "dmw_dialog1", "<- previous"); + SetLocalString(oMySpeaker, "dmw_function1", "conv_" + sConversation); + SetLocalString(oMySpeaker, "dmw_params1", "prev"); + SetLocalInt(oMySpeaker, "dmw_dialogprev", nLast); + } + + //fill the NPC's dialog spot + //(saved for last because the build process tromps on it) + dmwand_BuildConversationDialog(0, 0, sConversation, sParams); + } + else + { + //fill the NPC's dialog spot + dmwand_BuildConversationDialog(0, 0, sConversation, sParams); + + //No parameters specified, start at the top of the conversation + if(sParams == "") + { + nChoice = 1; + nCurrent = 1; + } + + //A "next->" choice was selected + if(TestStringAgainstPattern(sParams, "next")) + { + //get the number choice to start with + nCurrent = GetLocalInt(oMySpeaker, "dmw_dialognext"); + + //set this as the number for the "previous" choice to use + SetLocalInt(oMySpeaker, "dmw_dialogprev", nCurrent); + + //Set the first dialog choice to be "previous" + nChoice = 2; + SetLocalString(oMySpeaker, "dmw_dialog1", "<- Previous"); + SetLocalString(oMySpeaker, "dmw_function1", "conv_" + sConversation); + SetLocalString(oMySpeaker, "dmw_params1", "prev"); + } + + //Loop through to build the dialog list + for(;nChoice <= 10; nChoice++) + { + nMatch = dmwand_BuildConversationDialog(nCurrent, nChoice, sConversation, sParams); + //nLast will be the value of the choice before the last one + nLast = nTemp; + nTemp = nMatch; + if(nMatch > 0) { nCurrent = nMatch; } + if(nMatch == 0) { nLast = 0; } + nCurrent++; + } + + //If there were enough choices to fill 10 spots, make spot 9 a "next" + if(nLast > 0) + { + SetLocalString(oMySpeaker, "dmw_dialog9", "Next ->"); + SetLocalString(oMySpeaker, "dmw_function9", "conv_" + sConversation); + SetLocalString(oMySpeaker, "dmw_params9", "next"); + SetLocalInt(oMySpeaker, "dmw_dialognext", nLast); + } + } +} + +int dmw_conv_ListPlayers(int nCurrent, int nChoice, string sParams = "") +{ + string sText = ""; + string sCall = ""; + string sCallParams = ""; + object oPlayer; + int nCache; + + if((! TestStringAgainstPattern(sParams, "next")) && (! TestStringAgainstPattern(sParams, "prev"))) + { + //This is the first time running this function, so cache the objects + // of all players... we don't want our list swapping itself around every + // time you change a page + SetLocalString(oMySpeaker, "dmw_playerfunc", sParams); + int nCount = 1; + oPlayer = GetFirstPC(); + while(GetIsObjectValid(oPlayer)) + { + SetLocalObject(oMySpeaker, "dmw_playercache" + IntToString(nCount), oPlayer); + oPlayer = GetNextPC(); + nCount++; + } + nCount--; + SetLocalInt(oMySpeaker, "dmw_playercache", nCount); + } + + string sFunc = GetLocalString(oMySpeaker, "dmw_playerfunc"); + nCache = GetLocalInt(oMySpeaker, "dmw_playercache"); + + switch(nCurrent) + { + case 0: + nCurrent = 0; + sText = "Who would you like to work on?"; + sCall = ""; + sCallParams = ""; + break; + default: + //Find the next player in the cache who is valid + oPlayer = GetLocalObject(oMySpeaker, "dmw_playercache" + IntToString(nCurrent)); + while((! GetIsObjectValid(oPlayer)) && (nCurrent <= nCache)) + { + nCurrent++; + oPlayer = GetLocalObject(oMySpeaker, "dmw_playercache" + IntToString(nCurrent)); + } + + if(nCurrent > nCache) + { + //We've run out of cache, any other spots in this list should be + //skipped + nCurrent = 0; + sText = ""; + sCall = ""; + sCallParams = ""; + } + else + { + //We found a player, set up the list entry + sText = GetName(oPlayer) + " (" + GetPCPlayerName(oPlayer) + ")"; + sCall = sFunc; + sCallParams = IntToString(nCurrent); + } + break; + } + + SetLocalString(oMySpeaker, "dmw_dialog" + IntToString(nChoice), sText); + SetLocalString(oMySpeaker, "dmw_function" + IntToString(nChoice), sCall); + SetLocalString(oMySpeaker, "dmw_params" + IntToString(nChoice), sCallParams); + + return nCurrent; +} + +void dmwand_DoDialogChoice(int nChoice) +{ + string sCallFunction = GetLocalString(oMySpeaker, "dmw_function" + IntToString(nChoice)); + string sCallParams = GetLocalString(oMySpeaker, "dmw_params" + IntToString(nChoice)); + string sNav = ""; + + string sStart = GetStringLeft(sCallFunction, 5); + int nLen = GetStringLength(sCallFunction) - 5; + string sCall = GetSubString(sCallFunction, 5, nLen); + + if(TestStringAgainstPattern("conv_", sStart)) + { + dmwand_BuildConversation(sCall, sCallParams); + } + else + { + + if(TestStringAgainstPattern("PlayerListConv", sCall)) + { + dmwand_PlayerListConv(sCallParams); + return; + } + + if(TestStringAgainstPattern("Toad", sCall)) + { + DMFI_toad(oMyTarget, oMySpeaker); + return; + } + if(TestStringAgainstPattern("Untoad", sCall)) + { + DMFI_untoad(oMyTarget, oMySpeaker); + return; + } + if(TestStringAgainstPattern("KickPC", sCall)) + { + dmwand_KickPC(oMyTarget, oMySpeaker); + return; + } + + if(TestStringAgainstPattern("JumpPlayerHere", sCall)) + { + dmwand_JumpPlayerHere(); + return; + } + if(TestStringAgainstPattern("JumpToPlayer", sCall)) + { + dmwand_JumpToPlayer(); + return; + } + if(TestStringAgainstPattern("JumpPartyHere", sCall)) + { + dmwand_JumpPartyHere(); + return; + } + } +} diff --git a/_content/_hak/rune_prc8_top/j_inc_basic.nss b/_content/_hak/rune_prc8_top/j_inc_basic.nss new file mode 100644 index 0000000..91c4280 --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_basic.nss @@ -0,0 +1,582 @@ +/************************ [Include - Basic AI] ********************************* + Filename: J_INC_BASIC +************************* [Include - Basic AI] ********************************* + A few functions - mostly for animations and AI files. + + This is included in: + - All Basic AI files + - Any custom AI files in the packages you import. + + - Includes a few fighting things that help in combat (like Healing functions) +************************* [Workings] ******************************************* + A few settings for animations, as well as some AI combat functions to help + make custom combat easier. +************************* [Include - Basic AI] ********************************/ + +// Special: Bioware SoU Waypoints/Animations constants +// - Here so I know where they are :-P +const string sAnimCondVarname = "NW_ANIM_CONDITION"; +// This is the name of the local variable that holds the spawn-in conditions +string sSpawnCondVarname = "NW_GENERIC_MASTER"; + +// AI for waypoints +const string FILE_WALK_WAYPOINTS = "j_ai_walkwaypoin"; +const string WAYPOINT_RUN = "WAYPOINT_RUN"; +const string WAYPOINT_PAUSE = "WAYPOINT_PAUSE"; + +// Bioware constants. +const int NW_FLAG_STEALTH = 0x00000004; +const int NW_FLAG_SEARCH = 0x00000008; +const int NW_FLAG_AMBIENT_ANIMATIONS = 0x00080000; +const int NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS = 0x00200000; +const int NW_FLAG_DAY_NIGHT_POSTING = 0x00400000; +const int NW_FLAG_AMBIENT_ANIMATIONS_AVIAN = 0x00800000; + + +// Mark that the given creature has the given condition set for anitmations +// * Bioware SoU animations thing. +void SetAnimationCondition(int nCondition, int bValid = TRUE, object oCreature = OBJECT_SELF); + +// Sets the specified spawn-in condition on the caller as directed. +// * Only used for animations +void SetSpawnInCondition(int nCondition, int bValid = TRUE); + +// Base for moving round thier waypoints +// - Uses ExectuteScript to run the waypoint walking. +void SpawnWalkWayPoints(int nRun = FALSE, float fPause = 1.0); + +// Sets the custom AI file to sString to use. +// - "AI_TEMP_SET_TARGET" is set to anything passed to this script. +void SetAIFileName(string sString); + +// Gets the custom AI file set to us. +string GetAIFileName(); + +// Determines a combat round. +void DetermineCombatRound(object oTarget = OBJECT_INVALID); + +// Returns any pre-set target object +object Combat_GetAITargetObject(); +// Sets the pre-set target object to attack +void SetAITargetObject(object oTarget); + +// Gets the nearest seen or heard enemy. +// * bSeenOnly - If set to TRUE, it will only return a valid seen enemy (if any!) +object Combat_GetNearestSeenOrHeardEnemy(int bSeenOnly = FALSE); +// Gets if the object is valid, and we can see, or hear it. +// * bSeenOnly - If set to TRUE, it must be seen. +// TRUE if oTarget is valid. +int Combat_GetTargetValid(object oTarget, int bSeenOnly = FALSE); + +// This will call a random voicechat to be called, if iPercent is met. +void Combat_Taunt(int iPercent = 10); + +// This will heal oTarget with the best spell possible. +// - Like the Bioware function. +// - Will force healing if bForce is TRUE, ELSE, it will only heal at 50% HP. +// - TRUE if it heals oTarget +int Combat_HealTarget(object oTarget, int bForce = FALSE); +// This will loop all seen allies. If any of them need healing, it will heal +// them and return TRUE. +// - Uses Combat_HealTarget to check if they need healing. +int Combat_HealAllies(); + +// Only for use in "Combat_TurnUndead", it checks if there are any non-turned +// undead within 10M, of iRace. +int Combat_TurningAnyOfRaceValid(int iRace); + +// This will check if we can turn undead, and check if there are anythings we can +// turn, and turn if so. Uses mainly Bioware stuff. +int Combat_TurnUndead(); + +// This attempt to use the best potions the creature has. +// * Will return FALSE if they use none, or they already have the effects +// * Uses any Potection First (EG: stoneskin), then Benificial (EG: Bulls strength) +// then Enhancement (EG: Invisibility) +int Combat_UseAnyPotions(); + +// Attack oTarget with a melee weapon, and melee feats if we can hit them. +// - VERY basic! +void Combat_AttackMelee(object oTarget); +// Attack oTarget with a ranged weapon (if we have any), and ranged feats if we can hit them. +// - VERY basic! +void Combat_AttackRanged(object oTarget); + +// This will check if the caller has nSpell, and casts it at oObject if so. +// - Will not cast if oTarget has the effect of nSpell. +// - Returns TRUE if they cast nSpell. +int Combat_CastAtObject(int nSpell, object oTarget); +// This will check if the caller has nSpell, and casts it at oObject's location if so. +// - Will not cast if oTarget has the effect of nSpell. +// - Returns TRUE if they cast nSpell. +int Combat_CastAtLocation(int nSpell, object oTarget); +// Checks if tUse is TRUE, and uses it against oTarget if not got the effects. +int Combat_TalentAtObject(talent tUse, object oTarget); + +// Cheat-Casts nSpell, if under iPercent. +// * Doesn't cast if iPercent fails, or oTarget has nSpell's effects. +// Use this to make sure a caster doesn't run out of spells. +int Combat_CheatRandomSpellAtObject(int nSpell, object oTarget, int iPercent); + +// This will loop oTarget's effects, and return TRUE if any are equal to +// iEffect, which is a constant EFFECT_TYPE_* +int Combat_GetHasEffect(int iEffect, object oTarget = OBJECT_SELF); + +// This will walk the waypoints of the creature (re-activate them) +// Use this if the creature is not in combat/not attacking/no target to attack/ +void Combat_WalkWaypoints(); + +// Functions start. + +// Sets the specified spawn-in condition on the caller as directed. +void SetSpawnInCondition(int nCondition, int bValid = TRUE) +{ + int nSpawnInConditions = GetLocalInt(OBJECT_SELF, sSpawnCondVarname); + if(bValid == TRUE) + { + // Add the given spawn-in condition + nSpawnInConditions = nSpawnInConditions | nCondition; + SetLocalInt(OBJECT_SELF, sSpawnCondVarname, nSpawnInConditions); + } + else if (bValid == FALSE) + { + // Remove the given spawn-in condition + nSpawnInConditions = nSpawnInConditions & ~nCondition; + SetLocalInt(OBJECT_SELF, sSpawnCondVarname, nSpawnInConditions); + } +} + +// Mark that the given creature has the given condition set +// * Bioware SoU animations thing. +void SetAnimationCondition(int nCondition, int bValid = TRUE, object oCreature = OBJECT_SELF) +{ + int nCurrentCond = GetLocalInt(oCreature, sAnimCondVarname); + if (bValid) { + SetLocalInt(oCreature, sAnimCondVarname, nCurrentCond | nCondition); + } else { + SetLocalInt(oCreature, sAnimCondVarname, nCurrentCond & ~nCondition); + } +} + +// Base for moving round thier waypoints +// - Uses ExectuteScript to run the waypoint walking. +void SpawnWalkWayPoints(int nRun = FALSE, float fPause = 1.0) +{ + SetLocalInt(OBJECT_SELF, WAYPOINT_RUN, nRun); + SetLocalFloat(OBJECT_SELF, WAYPOINT_PAUSE, fPause); + ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF); +} + +// Sets the custom AI file to sString to use. +// - "AI_TEMP_TARGET_OBJECT" is set to anything passed to this script. +void SetAIFileName(string sString) +{ + SetLocalString(OBJECT_SELF, "AI_FILENAME", sString); +} + +// Gets the custom AI file set to us. +string GetAIFileName() +{ + return GetLocalString(OBJECT_SELF, "AI_FILENAME"); +} + +// Determines a combat round. +void DetermineCombatRound(object oTarget = OBJECT_INVALID) +{ + // Set local object + SetAITargetObject(oTarget); + // Execute the AI file set. + ExecuteScript(GetAIFileName(), OBJECT_SELF); +} + +// Returns any pre-set target object +object Combat_GetAITargetObject() +{ + return GetLocalObject(OBJECT_SELF, "AI_TEMP_SET_TARGET"); +} + +// Sets the pre-set target object to attack +void SetAITargetObject(object oTarget) +{ + SetLocalObject(OBJECT_SELF, "AI_TEMP_SET_TARGET", oTarget); +} + +// Gets the nearest seen or heard enemy. +object Combat_GetNearestSeenOrHeardEnemy(int bSeenOnly = FALSE) +{ + object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oTarget) && bSeenOnly == FALSE) + { + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oTarget)) + { + return OBJECT_INVALID; + } + } + return oTarget; +} +// Gets if the object is valid, and we can see, or hear it. +// * bSeenOnly - If set to TRUE, it must be seen. +// TRUE if oTarget is valid. +int Combat_GetTargetValid(object oTarget, int bSeenOnly = FALSE) +{ + // Check if valid + if(!GetIsObjectValid(oTarget)) return FALSE; + + // Check if seen + if(!GetObjectSeen(oTarget) && bSeenOnly == TRUE) return FALSE; + + // Check if heard + if(!GetObjectHeard(oTarget)) return FALSE; + + // Valid == TRUE + return TRUE; +} + +// This will call a random voicechat to be called, if iPercent is met. +void Combat_Taunt(int iPercent = 10) +{ + if(d100() <= iPercent) + { + int iVoice = VOICE_CHAT_BATTLECRY1; + switch(d6()) + { + case 1: iVoice = VOICE_CHAT_ATTACK; break; + case 2: iVoice = VOICE_CHAT_BATTLECRY1; break; + case 3: iVoice = VOICE_CHAT_BATTLECRY2; break; + case 4: iVoice = VOICE_CHAT_BATTLECRY3; break; + case 5: iVoice = VOICE_CHAT_LAUGH; break; + case 6: iVoice = VOICE_CHAT_TAUNT; break; + } + PlayVoiceChat(iVoice); + } +} + +// This will heal oTarget with the best spell possible. +// - Like the Bioware function. +// - Will force healing if bForce is TRUE, ELSE, it will only heal at 50% HP. +// - TRUE if it heals oTarget +int Combat_HealTarget(object oTarget, int bForce = FALSE) +{ + // Taken from Bioware AI and modified. + talent tUse; + int nCurrent = GetCurrentHitPoints(OBJECT_SELF) * 2; + int nBase = GetMaxHitPoints(OBJECT_SELF); + + // Check HP. + if( (nCurrent < nBase) || (bForce == TRUE) ) + { + if(oTarget == OBJECT_SELF) + { + tUse = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_POTION, 20); + if(Combat_TalentAtObject(tUse, oTarget)) + { + return TRUE; + } + } + tUse = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH, 20); + if(Combat_TalentAtObject(tUse, oTarget)) + { + return TRUE; + } + else + { + tUse = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_AREAEFFECT, 20); + if(Combat_TalentAtObject(tUse, oTarget)) + { + return TRUE; + } + } + } + return FALSE; +} +// This will loop all seen allies. If any of them need healing, it will heal +// them and return TRUE. +// - Uses Combat_HealTarget to check if they need healing. +int Combat_HealAllies() +{ + int iCnt = 1; + // Loop seen allies who are not dead + object oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, iCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); + while(GetIsObjectValid(oAlly)) + { + if(Combat_HealTarget(oAlly)) + { + // Stop - healed someone + return TRUE; + } + iCnt++; + oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, iCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); + } + return FALSE; +} + +// Only for use in "Combat_TurnUndead", it checks if there are any non-turned +// undead within 10M, of iRace. +int Combat_TurningAnyOfRaceValid(int iRace) +{ + int nCnt = 1; + int nCount = 0; + object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_RACIAL_TYPE, iRace); + while(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= 10.0) + { + if(!Combat_GetHasEffect(EFFECT_TYPE_TURNED, oTarget) && !GetIsDead(oTarget)) + { + return TRUE; + } + nCnt++; + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_RACIAL_TYPE, iRace); + } + return FALSE; +} + +// This will check if we can turn undead, and check if there are anythings we can +// turn, and turn if so. Uses mainly Bioware stuff. +int Combat_TurnUndead() +{ + if(GetHasFeat(FEAT_TURN_UNDEAD)) + { + object oUndead = Combat_GetNearestSeenOrHeardEnemy(); + if(Combat_GetHasEffect(EFFECT_TYPE_TURNED, oUndead) || + GetHitDice(OBJECT_SELF) <= GetHitDice(oUndead)) + { + return FALSE; + } + int nCount; + int nElemental = GetHasFeat(FEAT_AIR_DOMAIN_POWER) + + GetHasFeat(FEAT_EARTH_DOMAIN_POWER) + + GetHasFeat(FEAT_FIRE_DOMAIN_POWER) + + GetHasFeat(FEAT_FIRE_DOMAIN_POWER); + int nVermin = GetHasFeat(FEAT_PLANT_DOMAIN_POWER) + + GetHasFeat(FEAT_ANIMAL_COMPANION); + int nConstructs = GetHasFeat(FEAT_DESTRUCTION_DOMAIN_POWER); + int nOutsider = GetHasFeat(FEAT_GOOD_DOMAIN_POWER) + + GetHasFeat(FEAT_EVIL_DOMAIN_POWER) + + GetHasFeat(854); // planar turning + + if(nElemental == TRUE) + nCount += Combat_TurningAnyOfRaceValid(RACIAL_TYPE_ELEMENTAL); + + if(nVermin == TRUE) + nCount += Combat_TurningAnyOfRaceValid(RACIAL_TYPE_VERMIN); + + if(nOutsider == TRUE) + nCount += Combat_TurningAnyOfRaceValid(RACIAL_TYPE_OUTSIDER); + + if(nConstructs == TRUE) + nCount += Combat_TurningAnyOfRaceValid(RACIAL_TYPE_CONSTRUCT); + + nCount += Combat_TurningAnyOfRaceValid(RACIAL_TYPE_UNDEAD); + + if(nCount > 0) + { + ClearAllActions(); + ActionUseFeat(FEAT_TURN_UNDEAD, OBJECT_SELF); + return TRUE; + } + } + return FALSE; +} + +// This attempt to use the best potions the creature has. +// * Will return FALSE if they use none, or they already have the effects +// * Uses any Potection First (EG: stoneskin), then Enhancement (EG: Bulls strength) +// then Conditional (EG: Clarity) +int Combat_UseAnyPotions() +{ + talent tPotion = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_PROTECTION_POTION, 20); + + // Get if valid, and not got the effects + if(Combat_TalentAtObject(tPotion, OBJECT_SELF)) + { + return TRUE; + } + + // Else get the next one, Enhancement + tPotion = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_POTION, 20); + // Get if valid, and not got the effects + if(Combat_TalentAtObject(tPotion, OBJECT_SELF)) + { + return TRUE; + } + + // Else get the next one, Conditional + tPotion = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_POTION, 20); + // Get if valid, and not got the effects + if(Combat_TalentAtObject(tPotion, OBJECT_SELF)) + { + return TRUE; + } + // No potion used/no potion not got effect of. + return FALSE; +} + +// Attack oTarget with a melee weapon, and melee feats if we can hit them. +// - VERY basic! +void Combat_AttackMelee(object oTarget) +{ + // Equip best + ClearAllActions(); + ActionEquipMostDamagingMelee(oTarget); + + // Attack with feat if we can hit them + int iRandom = 5 + d10(); + if(GetBaseAttackBonus(OBJECT_SELF) + iRandom >= GetAC(oTarget)) + { + // Getting the melee talent category for melee feats is useful in a short + // AI script. Not useful in a longer one. + // - We can get feats Knockdown (Improved), Disarm (Improved), Sap, + // Stunning fist, Expertise (Improved), Flurry of Blows, Called Shot, + // and Power Attack (Improved) from this talent + talent tMelee = GetCreatureTalentRandom(TALENT_CATEGORY_HARMFUL_MELEE); + + // Can't use ranged feats - and make sure the feat is valid + if(GetIsTalentValid(tMelee) && + GetTypeFromTalent(tMelee) == TALENT_TYPE_FEAT) + { + int iTalentID = GetIdFromTalent(tMelee); + if(iTalentID == FEAT_RAPID_SHOT) + { + // Can't use ranged feats in melee, so just normal attack + ActionAttack(oTarget); + } + else + { + // Else, use the feat, and attack + ActionUseTalentOnObject(tMelee, oTarget); + } + } + } + else + { + ActionAttack(oTarget); + } +} +// Attack oTarget with a ranged weapon (if we have any), and ranged feats if we can hit them. +// - VERY basic! +void Combat_AttackRanged(object oTarget) +{ + // Equip best + ClearAllActions(); + ActionEquipMostDamagingRanged(oTarget); + + // Check if we did equip a ranged + if(!GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND))) + { + ActionAttack(oTarget); + return; + } + + // Attack with feat if we can hit them + int iRandom = 5 + d10(); + if(GetBaseAttackBonus(OBJECT_SELF) >= GetAC(oTarget) - iRandom) + { + // Feats for Range + if(GetHasFeat(FEAT_RAPID_SHOT)) + { + ActionUseFeat(FEAT_RAPID_SHOT, oTarget); + return; + } + else if(GetHasFeat(FEAT_CALLED_SHOT)) + { + ActionUseFeat(FEAT_CALLED_SHOT, oTarget); + return; + } + else + { + ActionAttack(oTarget); + } + } + else + { + ActionAttack(oTarget); + } +} + + +// This will check if the caller has nSpell, and casts it at oObject if so. +int Combat_CastAtObject(int nSpell, object oTarget) +{ + if(GetHasSpell(nSpell) && !GetHasSpellEffect(nSpell, oTarget)) + { + ClearAllActions(); + ActionCastSpellAtObject(nSpell, oTarget); + return TRUE; + } + return FALSE; +} +// This will check if the caller has nSpell, and casts it at oObject's location if so. +int Combat_CastAtLocation(int nSpell, object oTarget) +{ + if(GetHasSpell(nSpell) && !GetHasSpellEffect(nSpell, oTarget)) + { + ClearAllActions(); + ActionCastSpellAtLocation(nSpell, GetLocation(oTarget)); + return TRUE; + } + return FALSE; +} + +// Checks if tUse is TRUE, and uses it against oTarget if not got the effects. +int Combat_TalentAtObject(talent tUse, object oTarget) +{ + if(GetIsTalentValid(tUse)) + { + int iType = GetTypeFromTalent(tUse); + int iID = GetIdFromTalent(tUse); + // If it is a feat, check if they have the effect. + if(iType == TALENT_TYPE_FEAT && GetHasFeatEffect(iID, oTarget)) + { + return FALSE; + } + // If a spell, check if got the spell effect + else if(iType == TALENT_TYPE_SPELL && GetHasSpellEffect(iID, oTarget)) + { + return FALSE; + } + // Use it. + ClearAllActions(); + ActionUseTalentOnObject(tUse, oTarget); + return TRUE; + } + return FALSE; +} + +// Cheat-Casts nSpell, if under iPercent. +// * Doesn't cast if iPercent fails, or oTarget has nSpell's effects. +// Use this to make sure a caster doesn't run out of spells. +int Combat_CheatRandomSpellAtObject(int nSpell, object oTarget, int iPercent) +{ + // Check % + if(d100() <= iPercent && !GetHasSpellEffect(nSpell, oTarget)) + { + // Cheat cast it at oTarget + ClearAllActions(); + ActionCastSpellAtObject(nSpell, oTarget, METAMAGIC_ANY, TRUE); + return TRUE; + } + return FALSE; +} + +// This will loop oTarget's effects, and return TRUE if any are equal to +// iEffect, which is a constant EFFECT_TYPE_* +int Combat_GetHasEffect(int iEffect, object oTarget = OBJECT_SELF) +{ + effect eCheck = GetFirstEffect(oTarget); + while(GetIsEffectValid(eCheck)) + { + if(GetEffectType(eCheck) == iEffect) + { + return TRUE; + } + eCheck = GetNextEffect(oTarget); + } + return FALSE; +} + +// This will walk the waypoints of the creature (re-activate them) +// Use this if the creature is not in combat/not attacking/no target to attack/ +void Combat_WalkWaypoints() +{ + ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF); +} diff --git a/_content/_hak/rune_prc8_top/j_inc_constants.nss b/_content/_hak/rune_prc8_top/j_inc_constants.nss new file mode 100644 index 0000000..4f08b0e --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_constants.nss @@ -0,0 +1,2189 @@ +/************************ [Constants] ****************************************** + Filename: j_inc_constants +************************* [Constants] ****************************************** + This file just holds many constants, and many common functions (EG: Set + and Get spawn in conditions) to stop repeating code. + + See workings for more information. +************************* [History] ******************************************** + 1.3 - Added to make sure things are not repeated + constants in one place +************************* [Workings] ******************************************* + By using the SoU implimented way of defining static variables, "const" means + I can have a whole include which will include all used variables - this + keeps it all uniformed, and compiles better - as in all variable names + will be valid. + + It may also help speed, as variables which are repeated, are going to be + there to pick up from memory. + + If it does cause a lot of lag, more updates should clean this up. + + - To Do - Impliment better Debug system (see j_inc_debug) which you can + uncomment them long debug strings so that they don't get added to compiled + scripts if not used. +************************* [Arguments] ****************************************** + Arguments: N/A +************************* [Constants] *****************************************/ + +// This makes most scripts have debug anyway, and so its there :-). +// Multiple includes are ignored anyway :-D +#include "j_inc_debug" + +// This is a seperate script from all others. It is executed if a creature wants +// to DetermineCombatRound. It must be seperate to have override and non-override +// versions working together. +const string COMBAT_FILE = "j_ai_detercombat"; // FILENAME + +// Byond that, do NOT change the rest of these constants, unless you know +// what you are doing! +// I recommend not adding to this file either. Making your own constants +// file will make it full-proof. +//nw_walk_wp +// - FILE_ +const string FILE_WALK_WAYPOINTS = "j_ai_walkwaypoin"; // FILENAME +const string FILE_RE_SET_WEAPONS = "j_ai_setweapons"; // FILENAME +// If we are dead when this fires, it destroys us +const string FILE_DEATH_CLEANUP = "j_ai_destroyself"; // FILENAME + +// Heartbeat files. Speeds up (hopefully!) heartbeat calls and cirtainly heartbeat file size. +const string FILE_HEARTBEAT_TALENT_BUFF = "j_ai_heart_buff"; +const string FILE_HEARTBEAT_LOOT = "j_ai_heart_loot"; +const string FILE_HEARTBEAT_ANIMATIONS = "j_ai_heart_aimate"; +const string FILE_HEARTBEAT_WALK_TO_PC = "j_ai_heart_serch"; + +const string FILE_DRAGON_WING_BUFFET = "j_ai_wingbuffet"; +const string FILE_FLY_ATTACK = "j_ai_wingflying"; + +// Spell trigger creation file +const string FILE_SPELLTRIGGER_START = "j_ai_spelltrig1"; + +// Special: custom AI script set to a standard local string to this: +const string AI_CUSTOM_AI_SCRIPT = "AI_CUSTOM_AI_SCRIPT"; + +/******************************************************************************/ +// Simple constants to save re-typing ETC. +/******************************************************************************/ +// Just trying this. It won't harm anything, as with the const word, if the +// numbers are not used, they are not added, but if they are, getting them from +// these might be better. +// - Especially used for imputting the spell talent numbers, and required +// INT, WIS or CHA for that spell. :-D +const int iM10 = -10;// Dying threshold - die at -11 +const int iM1 = -1; +const int i0 = 0; +const int i1 = 1; +const int i2 = 2; +const int i3 = 3; +const int i4 = 4; +const int i5 = 5; +const int i6 = 6; +const int i7 = 7; +const int i8 = 8; +const int i9 = 9; +const int i10 = 10; +const int i11 = 11; +const int i12 = 12; +const int i13 = 13; +const int i14 = 14; +const int i15 = 15; +const int i16 = 16; +const int i17 = 17; +const int i18 = 18; +const int i19 = 19; +const int i20 = 20; +const int i21 = 21; +const int i22 = 22; +const int i23 = 23;// SpellAllSpells +const int i24 = 24; +const int i25 = 25; +const int i30 = 30; +const int i35 = 35; +const int i40 = 40;// Here onwards are normally %'s +const int i50 = 50; +const int i60 = 60; +const int i70 = 70; +const int i80 = 80; +const int i90 = 90; +const int i99 = 99; +const int i100 = 100; +const int i150 = 150;// Limit for PW Stun + +const float f0 = 0.0; +const float f1 = 1.0; +const float f2 = 2.0; +const float f3 = 3.0; +const float f4 = 4.0; +const float f5 = 5.0; +const float f6 = 6.0;// 1 heartbeat/round +const float f8 = 8.0;// Useful...for some reason +const float f9 = 9.0; +const float f10 = 10.0; +const float f11 = 11.0;// Range for some spell cones +const float f12 = 12.0;// 2 heartbeats/rounds +const float f15 = 15.0; +const float f16 = 16.0;// Double f8, the range of summon spells. +const float f18 = 18.0;// 3 heartbeats/rounds +const float f20 = 20.0; +const float f24 = 24.0;// 4 heartbeats/rounds +const float f30 = 30.0; +const float f35 = 35.0; +const float f40 = 40.0; +const float f50 = 50.0; +const float f60 = 60.0; + +// Ranges for spells - from Ranges.2da +const float fTouchRange = 2.25; +const float fShortRange = 8.0; +const float fMediumRange = 20.0; +const float fLongRange = 40.0; + +// Types: +// S. = Stored +// FILENAME +// CONSTANT +// S.CONSTANT +// S.INTEGER +// S.OBJECT +// S.FLOAT + +/******************************************************************************/ +// Global overriding actions +/******************************************************************************/ +// Leader thing +const int AI_SPECIAL_ACTIONS_ME_RUNNER = 1; // CONSTANT +// Fleeing +const int AI_SPECIAL_ACTIONS_FLEE = 2; // CONSTANT +// Moving out of a pre-set AOE. +const int AI_SPECIAL_ACTIONS_MOVE_OUT_OF_AOE = 3; // CONSTANT + +// This is trap thing. If we (on heartbeat) start disarming a trap, can we +// ignore it in combat? +const string TRAP_CAN_IGNORE_IN_COMBAT = "TRAP_CAN_IGNORE_IN_COMBAT";// S.INTEGER + +// Override for all AI scripts +const string AI_TOGGLE = "AI_TOGGLE"; // INTEGER + +// Set to a local int... +// 1 = Beholder AI +// 2 = Mindflayer AI +const string AI_SPECIAL_AI = "AI_SPECIAL_AI"; + +// Set to the last AI spell category cast. +const string ITEM_TALENT_VALUE = "ITEM_TALENT_VALUE"; // S.CONSTANT + + /*************************** Constant set-up ******************************* + We set up all global Spawn In Constants - hex numbers, used to check + one integer stored OnSpawn for settings the AI should use. + + Also constants for strings, and local variables. + **************************** Constant set-up ******************************/ + +/******************************************************************************/ +// String constants not associated with On Spawn options. Usually values. +/******************************************************************************/ +const string AI_INTELLIGENCE = "AI_INTELLIGENCE"; // S.INTEGER +const string AI_MORALE = "AI_MORALE"; // S.INTEGER +// Used for spontaeous spells, in the combat AI include and spell files. +const string AI_SPONTAEUOUSLY_CAST_HEALING = "AI_SPONTAEUOUSLY_CAST_HEALING";// S.INTEGER +// The last level of what summoned animal we cast. +const string AI_LAST_SUMMONED_LEVEL = "AI_LAST_SUMMONED_LEVEL"; // S.INTEGER +// Stores (if got FEAT_TURN_UNDEAD) the max level of undead we can turn. +const string AI_TURNING_LEVEL = "AI_TURNING_LEVEL"; // S.INTEGER +// Stores the amount of spell levels we are totally immune to +// - Set to a local int. +const string AI_SPELL_IMMUNE_LEVEL = "AI_SPELL_IMMUNE_LEVEL"; // S.INTEGER + +// Effects constants, actually set on the PC's too for performance :-) +const string AI_EFFECT_HEX = "AI_EFFECT_HEX"; +const string AI_SPELL_HEX = "AI_SPELL_HEX"; +const string AI_ABILITY_DECREASE = "AI_ABILITY_DECREASE"; +// Timer for resetting PC effects +const string AI_TIMER_EFFECT_SET = "AI_TIMER_EFFECT_SET"; +// This is not a timer, but if 1, it means the NPC uses Jasperre's AI and sets +// thier own effects +const string AI_JASPERRES_EFFECT_SET = "AI_JASPERRES_EFFECT_SET"; +// This is a timer for checking allies for spell effects, for buffing. +// - If TRUE, it won't loop around until it finds one to cast at. +// AI_TIMER_BUFF_ALLY_SPELL + IntToString(iSpell); +const string AI_TIMER_BUFF_ALLY_SPELL = "AI_TIMER_BUFF_ALLY_SPELL"; + +// When set to TRUE, it will stop heartbeat flee casting spells +// Deleted when we reach our flee target. +const string AI_HEARTBEAT_FLEE_SPELLS = "AI_HEARTBEAT_FLEE_SPELLS";// S.INTEGER + +// Timer for blocking creatures, and allies re-initiating combat +const string AI_TIMER_BLOCKED = "AI_TIMER_BLOCKED"; + +// this is set for 0.1 second to stop multiple DetermineCombatRound scripts being called +// in a short space of time. +const string AI_DEFAULT_AI_COOLDOWN = "AI_DEFAULT_AI_COOLDOWN"; + +// If this timer is set On Death, it means we do NOT apply EffectDeath to self. +const string AI_TIMER_DEATH_EFFECT_DEATH = "AI_TIMER_DEATH_EFFECT_DEATH"; + +/******************************************************************************/ +// Spell Trigger numbers/constants/settings +/******************************************************************************/ + +// Sets to a cirtain condition +const string SPELLTRIGGER_NOT_GOT_FIRST_SPELL = "AIST_FRST";// - When we !GetHasSpellEffect(iFirstSpell); +const string SPELLTRIGGER_DAMAGED_AT_PERCENT = "AIST_DAMA";// - When we are below X percent of HP, it fires +const string SPELLTRIGGER_IMMOBILE = "AIST_IMMB";// - When we are uncommandable/paralyzed/sleeping, etc. +const string SPELLTRIGGER_START_OF_COMBAT = "AIST_STCM";// - Fired first, whatever condition + +// Set up as: +// ASST_FRSTXY +// X - Spell trigger number (1-9) +// Y - Spell trigger spell stored (1-9) +// Local integer +// MAXINT_ASST_FRST1 +// Max number of spells in this trigger +// ASS_FRSTUSED +// This is set to TRUE if used up this round. +const string USED = "USED"; +// This is used for damage %, and is 1-100 for damage at % triggers. +const string VALUE = "VALUE"; +// Our spell trigger creature object +const string AI_SPELL_TRIGGER_CREATURE = "AI_SPELL_TRIGGER_CREATURE"; + +// When max is set to -1, we actually reset this on rest event. + +// Local on casters +// - Max no. of spells +const string AI_SPELLTRIG_MAXSPELLS = "AI_SPELLTRIG_MAXSPELLS"; +// - Constant for prefix - EG: AIST_IMMB3 for the third Immobile spell trig. +const string AI_SPELLTRIG_PREFIX = "AI_SPELLTRIG_PREFIX"; + +/******************************************************************************/ +// These are the "forgotten" constants, for spells/feats in SoU. +/******************************************************************************/ +// FEATS +// THese are the forgotten ones. Others are remembered. +// I have left OUT the ones which we cannot "use" + +// These are blackguard. Note that they are under spell abilities, but I'd rather +// use them as ActionUseFeat. +const int AI_FEAT_BG_CREATE_UNDEAD = 474; // CONSTANT +const int AI_FEAT_BG_FIENDISH_SERVANT = 475; // CONSTANT +// Othes +const int AI_FEAT_PM_CREATE_UNDEAD = 890; +const int AI_FEAT_PM_ANIMATE_DEAD = 889; +const int AI_FEAT_PM_CREATE_GREATER_UNDEAD = 895; + +// Polymorphing ones missing +const int AI_FEAT_EPIC_WILD_SHAPE_UNDEAD = 872; +const int AI_FEAT_EPIC_WILD_SHAPE_DRAGON = 873; +const int AI_FEAT_GREATER_WILDSHAPE_1 = 898; +const int AI_FEAT_GREATER_WILDSHAPE_2 = 900; +const int AI_FEAT_GREATER_WILDSHAPE_3 = 901; +const int AI_FEAT_HUMANOID_SHAPE = 902; +const int AI_FEAT_GREATER_WILDSHAPE_4 = 903; + +const int AI_FEAT_EPIC_OUTSIDER_SHAPE = 1060; +const int AI_FEAT_EPIC_CONSTRUCT_SHAPE = 1061; +const int AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_1 = 1062; +const int AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_2 = 1063; +const int AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_3 = 1064; +const int AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_4 = 1065; +const int AI_FEAT_EPIC_SHIFTER_INFINITE_HUMANOID_SHAPE = 1066; +const int AI_FEAT_EPIC_DRUID_INFINITE_WILDSHAPE = 1068; +const int AI_FEAT_EPIC_DRUID_INFINITE_ELEMENTAL_SHAPE = 1069; + +const int AI_FEAT_EPIC_PLANAR_TURNING = 854; + +// Used in tunr undead checking. Faster then looping effects - GetHasSpellEffect(AI_SPELL_FEAT_TURN_UNDEAD); +const int AI_SPELL_FEAT_TURN_UNDEAD = 308; + +const int AI_SPELL_EVIL_BLIGHT = 566; +const int AI_SPELL_FEAT_PLANAR_TURNING = 643; + +// Epic spells are feats, but act like spells. +const int AI_FEAT_EPIC_SPELL_MUMMY_DUST = 874; +// SPELL_EPIC_MUMMY_DUST +const int AI_FEAT_EPIC_SPELL_DRAGON_KNIGHT = 875; +// SPELL_EPIC_DRAGON_KNIGHT +const int AI_FEAT_EPIC_SPELL_HELLBALL = 876; +const int AI_FEAT_EPIC_SPELL_EPIC_MAGE_ARMOR = 877; +const int AI_FEAT_EPIC_SPELL_RUIN = 878; +const int AI_FEAT_EPIC_SPELL_EPIC_WARDING = 990; + +const int AI_SPELL_EPIC_WARDING = 695; + +// healing ones missed +// Harm self - undead heal! +const int AI_SPELLABILITY_UNDEAD_HARM_SELF = 759; +// Cure Others Critical Wounds +const int AI_SPELLABILITY_CURE_CRITICAL_WOUNDS_OTHER= 567; + + +// Subspell set to this: +const string AI_SPELL_SUB_SPELL_CAST = "AI_SPELL_SUB_SPELL_CAST"; + +// For checking if they have this spell's effects. Most are feats, if not all. +const int AI_SPELL_BARD_SONG = 411; // CONSTANT +const int AI_SPELL_CURSE_SONG = 644; // CONSTANT +// - hordes - still not in! +const int AI_SPELL_OWLS_INSIGHT = 438; // CONSTANT + +// These are not anywhere, even though blackguard and AA ones are +const int AI_SPELL_HARPER_CATS_GRACE = 481; +const int AI_SPELL_HARPER_EAGLE_SPLEDOR = 482; + +// Shifter only spells (monster abilities) that are limited +const int AI_SPELLABILITY_GWILDSHAPE_STONEGAZE = 687; +const int AI_SPELLABILITY_GWILDSHAPE_DRIDER_DARKNESS= 688; +const int AI_SPELLABILITY_GWILDSHAPE_SPIKES = 692;// Manticore Spikes - no limit +const int AI_SPELLABILITY_GWILDSHAPE_MINDBLAST = 693;// GWildShape_Mindblast +const int AI_SPELLABILITY_VAMPIRE_DOMINATION_GAZE = 800;// Dom gaze. + +// Special monster abilities/other hordes spells +// - Some are shifter. Marked by SHIFT at end +const int AI_SPELLABILITY_HARPYSONG = 686;// SHIFT - Harpysong +const int AI_SPELLABILITY_SUMMON_BAATEZU = 701; +const int AI_SPELLABILITY_EYEBALL_RAY_0 = 710;// EyeballRay0 +const int AI_SPELLABILITY_EYEBALL_RAY_1 = 711;// EyeballRay1 +const int AI_SPELLABILITY_EYEBALL_RAY_2 = 712;// EyeballRay2 +const int AI_SPELLABILITY_MINDFLAYER_MINDBLAST_10 = 713;// Mindflayer Mindblast 10 +const int AI_SPELLABILITY_MINDFLAYER_PARAGON_MINDBLAST = 714;// Mindflayer Paragon Mindblast +const int AI_SPELLABILITY_GOLEM_RANGED_SLAM = 715; +const int AI_SPELLABILITY_SUCKBRAIN = 716;// SuckBrain +const int AI_SPELLABILITY_BEHOLDER_MAGIC_CONE = 727;// Beholder_Anti_Magic_Cone +const int AI_SPELLABILITY_BEBELITH_WEB = 731;// Bebelith Web +const int AI_SPELLABILITY_BEHOLDER_ALLRAYS = 736;// Beholder_Special_Spell_AI +const int AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER = 741;// Psionic Inertial Barrier +const int AI_SPELLABILITY_SHADOWBLEND = 757;// ShadowBlend - Shadow dragon. +const int AI_SPELLABILITY_AURA_OF_HELLFIRE = 761;// Aura +const int AI_SPELLABILITY_HELL_INFERNO = 762;// Hell Inferno - worse then SPELL_INFERNO +const int AI_SPELLABILITY_PSIONIC_MASS_CONCUSSION = 763;// Damage to AOE - psiconic mass concussion +const int AI_SPELLABILITY_SHADOW_ATTACK = 769;// SHIFTER Shadow Attack - also shifter +const int AI_SPELLABILITY_SLAAD_CHAOS_SPITTLE = 770;// SHIFTER Slaad Chaos Spittle - also shifter +const int AI_SPELLABILITY_BATTLE_BOULDER_TOSS = 773; +const int AI_SPELLABILITY_PRISMATIC_DEFLECTING_FORCE= 774;// Deflecting_Force +const int AI_SPELLABILITY_GIANT_HURL_ROCK = 775; +const int AI_SPELLABILITY_ILLITHID_MINDBLAST = 789; +const int AI_SPELLABILITY_VAMPIRE_INVISIBILITY = 799;// SHIFTER Vampire Invis. +const int AI_SPELLABILITY_AZER_FIRE_BLAST = 801;// SHIFTER Fire Blast. +const int AI_SPELLABILITY_SHIFTER_SPECTRE_ATTACK = 802;// SHIFTER Spectire Attack. + +// Constants for AOE's +const string AI_AOE_PER_STORM = "AOE_PER_STORM"; +const string AI_AOE_PER_CREEPING_DOOM = "AOE_PER_CREEPING_DOOM"; +const string AI_AOE_PER_FOGSTINK = "AOE_PER_FOGSTINK"; +const string AI_AOE_PER_GREASE = "AOE_PER_GREASE"; +const string AI_AOE_PER_WALLFIRE = "AOE_PER_WALLFIRE"; +const string AI_AOE_PER_WALLBLADE = "AOE_PER_WALLBLADE"; +const string AI_AOE_PER_FOGACID = "VFX_PER_FOGACID"; +const string AI_AOE_PER_FOGFIRE = "VFX_PER_FOGFIRE"; +const string AI_AOE_PER_FOGKILL = "AOE_PER_FOGKILL"; +const string AI_AOE_PER_FOGMIND = "VFX_PER_FOGMIND"; +const string AI_AOE_PER_ENTANGLE = "VFX_PER_ENTANGLE"; +const string AI_AOE_PER_EVARDS_BLACK_TENTACLES = "VFX_PER_EVARDS_BLACK_TENTACLES"; +const string AI_AOE_PER_FOGBEWILDERMENT = "VFX_PER_FOGBEWILDERMENT"; +const string AI_AOE_PER_STONEHOLD = "VFX_PER_STONEHOLD"; +const string AI_AOE_PER_WEB = "VFX_PER_WEB"; + +// When we ActionAttack +const int AI_NORMAL_MELEE_ATTACK = 1000; // CONSTANT +const int AI_PARRY_ATTACK = 2000; // CONSTANT + +/******************************************************************************/ +// The ignore string. If a creature has this set to anything but 0, then the AI ignores them. +/******************************************************************************/ +const string AI_IGNORE_TOGGLE = "AI_IGNORE_TOGGLE"; // S.INTEGER + +/******************************************************************************/ +// String constants not associated with Different Workings during play. +/******************************************************************************/ + +// In Other AI, this is set on high damage +const string AI_MORALE_PENALTY = "AI_MORALE_PENALTY"; // S.INTEGER +// Set via. SetCurrentAction +const string AI_CURRENT_ACTION = "AI_CURRENT_ACTION"; // S.INTEGER +// This is set to TRUE once we have died once. +const string WE_HAVE_DIED_ONCE = "WE_HAVE_DIED_ONCE"; // S.INTEGER +// Amount of deaths. +const string AMOUNT_OF_DEATHS = "AMOUNT_OF_DEATHS"; // S.INTEGER +// Turns off death script firing at all - used for Bioware lootable stuff. +const string I_AM_TOTALLY_DEAD = "I_AM_TOTALLY_DEAD"; // S.INTEGER + +const string MAX_ELEMENTAL_DAMAGE = "MAX_ELEMENTAL_DAMAGE"; // S.INTEGER +const string LAST_ELEMENTAL_DAMAGE = "LAST_ELEMENTAL_DAMAGE"; // S.INTEGER +// For this, it sets AI_HIGHEST_DAMAGER to the damager. +const string AI_HIGHEST_DAMAGE_AMOUNT = "AI_HIGHEST_DAMAGE_AMOUNT";// S.INTEGER +// This needs the damager to be the stored "AI_STORED_LAST_ATTACKER" +const string AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT = "AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT";// S.INTEGER +// Healing kits +const string AI_VALID_HEALING_KIT_OBJECT = "AI_VALID_HEALING_KIT_OBJECT";// S.INTEGER +const string AI_VALID_HEALING_KITS = "AI_VALID_HEALING_KITS"; // S.INTEGER +// Set to TRUE before re-setting things, it ignores weapons, only does healing kits. +const string RESET_HEALING_KITS = "RESET_HEALING_KITS"; // S.INTEGER +// TRUE if any of the SoU animations are valid. +const string AI_VALID_ANIMATIONS = "AI_VALID_ANIMATIONS"; + +// The amounts set to these are waypoint things +const string WAYPOINT_RUN = "WAYPOINT_RUN"; +const string WAYPOINT_PAUSE = "WAYPOINT_PAUSE"; + +// S.FLOAT +/******************************************************************************/ +// Shout strings. +/******************************************************************************/ +// 1 - I was attacked. :-P +const string I_WAS_ATTACKED = "I_WAS_ATTACKED"; // CONSTANT +// 2 is blocked NWN thingy +// 3 - Call to arms - Determines combat round +const string CALL_TO_ARMS = "CALL_TO_ARMS"; // CONSTANT +// 4 - Runner shout (shouts on heartbeat if running) +const string HELP_MY_FRIEND = "HELP_MY_FRIEND"; // CONSTANT +// 5 - Leader flee now +const string LEADER_FLEE_NOW = "LEADER_FLEE_NOW"; // CONSTANT +// 6 - Attack target X (specific target) +const string LEADER_ATTACK_TARGET = "LEADER_ATTACK_TARGET"; // CONSTANT +// 7 - I was killed - May flee if lots die! +const string I_WAS_KILLED = "I_WAS_KILLED"; // CONSTANT +// 8 - 1.3 - PLaceables/doors which shout this get responded to! +const string I_WAS_OPENED = "I_WAS_OPENED"; // CONSTANT + +// Extra - runner location variable (local location) +const string AI_HELP_MY_FRIEND_LOCATION = "AI_HELP_MY_FRIEND_LOCATION";// LOCATION +// Set on a placeable, by the placeable - thier last opener. +const string PLACEABLE_LAST_OPENED_BY = "PLACEABLE_LAST_OPENED_BY";// Object + +/******************************************************************************/ +// All timers (the end bits. Not the prefixes.) +/******************************************************************************/ +const string AI_TIMER_TURN_OFF_HIDE = "AI_TURN_OFF_HIDE"; // S.INTEGER +const string AI_TIMER_JUST_CAST_INVISIBILITY = "AI_TIMER_JUST_CAST_INVISIBILITY";//S.INTEGER +const string AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID = "AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID";// S.INTEGER +// Heartbeat "move to PC" searching +const string AI_TIMER_SEARCHING = "AI_TIMER_SEARCHING"; // S.INTEGER +const string AI_TIMER_STILL_PICKING_UP_ITEMS = "AI_TIMER_STILL_PICKING_UP_ITEMS";// S.INTEGER +const string AI_TIMER_AIMATIONS_PAUSE = "AI_TIMER_AIMATIONS_PAUSE"; // S.INTEGER +const string AI_TIMER_ATTACKED_IN_HTH = "AI_TIMER_ATTACKED_IN_HTH"; // S.INTEGER +const string AI_TIMER_TAUNT = "AI_TIMER_TAUNT"; // S.INTEGER +const string AI_TIMER_EMPATHY = "AI_TIMER_EMPATHY"; // S.INTEGER +// Special: Adds AOE spell which caused the event. +const string AI_TIMER_AOE_SPELL_EVENT = "AI_TIMER_AOE_SPELL_EVENT"; +const string AI_TIMER_FLEE = "AI_TIMER_FLEE"; +const string AI_TIMER_LEADER_SENT_RUNNER = "AI_TIMER_LEADER_SENT_RUNNER"; + +// Special search timer - stops calling Search() +const string AI_TIMER_SEARCH = "AI_TIMER_SEARCH_TIMER"; // S.INTEGER + +// On phisical attacked - if knockdown adn they can hit us (and not doofing the AI) +// then we use healing sooner as, basically, we may be knockdowned too much! +const string AI_TIMER_KNOCKDOWN = "AI_TIMER_KNOCKDOWN_TIMER"; + + +// Timer to stop cure condition in AI firing. +const string AI_TIMER_CURE_CONDITION = "AI_TIMER_CURE_CONDITION"; + +/******************************************************************************/ +// All speak talk arrays +/******************************************************************************/ +const string AI_TALK_ON_PERCIEVE_ENEMY = "AI_TALK_ON_PERCIEVE_ENEMY";// S.STRING +const string AI_TALK_ON_CONVERSATION = "AI_TALK_ON_CONVERSATION"; // S.STRING +const string AI_TALK_ON_PHISICALLY_ATTACKED = "AI_TALK_ON_PHISICALLY_ATTACKED";// S.STRING +const string AI_TALK_ON_DAMAGED = "AI_TALK_ON_DAMAGED"; // S.STRING +const string AI_TALK_ON_DEATH = "AI_TALK_ON_DEATH"; // S.STRING +const string AI_TALK_ON_HOSTILE_SPELL_CAST_AT = "AI_TALK_ON_HOSTILE_SPELL_CAST_AT";// S.STRING +const string AI_TALK_ON_MORALE_BREAK = "AI_TALK_ON_MORALE_BREAK";// S.STRING +const string AI_TALK_ON_COMBAT_ROUND = "AI_TALK_ON_COMBAT_ROUND";// S.STRING +const string AI_TALK_WE_PASS_POTION = "AI_TALK_WE_PASS_POTION"; +const string AI_TALK_WE_GOT_POTION = "AI_TALK_WE_GOT_POTION"; +const string AI_TALK_ON_CANNOT_RUN = "AI_TALK_ON_CANNOT_RUN"; +const string AI_TALK_ON_STUPID_RUN = "AI_TALK_ON_STUPID_RUN"; +const string AI_TALK_ON_COMBAT_ROUND_EQUAL = "AI_TALK_ON_COMBAT_ROUND_EQUAL"; +const string AI_TALK_ON_COMBAT_ROUND_THEM_OVER_US = "AI_TALK_ON_COMBAT_ROUND_THEM_OVER_US"; +const string AI_TALK_ON_COMBAT_ROUND_US_OVER_THEM = "AI_TALK_ON_COMBAT_ROUND_US_OVER_THEM"; +const string AI_TALK_ON_TAUNT = "AI_TALK_ON_TAUNT"; + +const string AI_TALK_ON_LEADER_SEND_RUNNER = "AI_TALK_ON_LEADER_SEND_RUNNER"; +const string AI_TALK_ON_LEADER_ATTACK_TARGET = "AI_TALK_ON_LEADER_ATTACK_TARGET"; +// Constant for the Size of the a string array - the prefix. +const string ARRAY_SIZE = "ARRAY_SIZE_"; // S.STRING +const string ARRAY_PERCENT = "ARRAY_PER_"; // S.STRING + +// Time stop stored array. +const string TIME_STOP_LAST_ = "TIME_STOP_LAST_"; +const string TIME_STOP_LAST_ARRAY_SIZE = "TIME_STOP_LAST_ARRAY_SIZE"; + +/******************************************************************************/ +// Objects +/******************************************************************************/ +// THe leader shout sets an object for others to attack. +const string AI_ATTACK_SPECIFIC_OBJECT = "AI_ATTACK_SPECIFIC_OBJECT";// S.OBJECT +// Our flee object string name +const string AI_FLEE_TO = "AI_FLEE_TO"; // S.OBJECT +// Object - The AOE we have been set to move from - special action +const string AI_AOE_FLEE_FROM = "AI_AOE_FLEE_FROM"; // S.OBJECT +// - RANGE to flee from the above object +// - Stored as a normal float value. +const string AI_AOE_FLEE_FROM_RANGE = "AI_AOE_FLEE_FROM_RANGE"; // S.FLOAT +// Set to whatever we attack, so others can find out. +const string AI_TO_ATTACK = "AI_TO_ATTACK"; // S.OBJECT +// The last phisical attacker set. +const string AI_STORED_LAST_ATTACKER = "AI_STORED_LAST_ATTACKER";// S.OBJECT +// Highest damager +const string AI_HIGHEST_DAMAGER = "AI_HIGHEST_DAMAGER"; // S.OBJECT +// Last person (set on the perception of inaudible or invisible) to have +// invisibility. It is also a local location (no prefix) for where they were! +const string AI_LAST_TO_GO_INVISIBLE = "AI_LAST_TO_GO_INVISIBLE";// S.OBJECT +// Stored under AI object as normal. If valid, we are meant to run to it. +const string AI_RUNNER_TARGET = "AI_RUNNER_TARGET"; // S.OBJECT +/******************************************************************************/ +// Looting special +/******************************************************************************/ +// Current object to take the thing +const string AI_CURRENT_TAKER = "AI_CURRENT_TAKER"; // S.OBJECT +// Exact tag of default body bags. +const string BODY_BAG = "BodyBag"; + +/******************************************************************************/ +// Weapons. +/******************************************************************************/ +// Arrays, so we have an array (best to worst value) +const string AI_WEAPON_PRIMARY = "AI_WEAPON_PRIMARY"; // S.OBJECT +const string AI_WEAPON_SECONDARY = "AI_WEAPON_SECONDARY"; // S.OBJECT +const string AI_WEAPON_TWO_HANDED = "AI_WEAPON_TWO_HANDED"; // S.OBJECT +const string AI_WEAPON_RANGED = "AI_WEAPON_RANGED"; // S.OBJECT +const string AI_WEAPON_RANGED_2 = "AI_WEAPON_RANGED_2"; // S.OBJECT +const string AI_WEAPON_RANGED_SHIELD = "AI_WEAPON_RANGED_SHIELD";// S.INTEGER +const string AI_WEAPON_RANGED_AMMOSLOT = "AI_WEAPON_RANGED_AMMOSLOT";// S.INTEGER +const string AI_WEAPON_RANGED_IS_UNLIMITED = "AI_WEAPON_RANGED_IS_UNLIMITED";// S.INTEGER +const string AI_WEAPON_SHIELD = "AI_WEAPON_SHIELD"; // S.OBJECT +const string AI_WEAPON_SHIELD_2 = "AI_WEAPON_SHIELD_2"; // S.OBJECT +// End post-fixs as it were +const string WEAP_SIZE = "AI_WEAP_SIZE"; // CONSTANT +const string WEAP_DAMAGE = "AI_WEAP_DAMAGE"; // CONSTANT + +/******************************************************************************* + Constants for spawn options + + These are set via. SetSpawnInCondition. They have sName set to the right + one else it is never picked up. + + Names: + + - Targeting & Fleeing (AI_TARGETING_FLEE_MASTER) + - Fighting & Spells (AI_COMBAT_MASTER) + - Other Combat - Healing, Skills & bosses (AI_OTHER_COMBAT_MASTER) + - Other - Death corpses, minor things (AI_OTHER_MASTER) + - User Defined (AI_UDE_MASTER) + - Shouts N/A + - Default Bioware settings (NW_GENERIC_MASTER) +*******************************************************************************/ + +/******************************************************************************/ +/******************************************************************************/ +//Master Constants for UDEs +/******************************************************************************/ +/******************************************************************************/ +const string AI_UDE_MASTER = "AI_UDE_MASTER"; +const string EXIT_UDE_PREFIX_ = "EXIT_UDE_PREFIX_";// Exit string, like EXIT_UDE_PREFIX_1001 is the heartbeat exit +/******************************************************************************/ +/******************************************************************************/ + +// Added pre- events. Starts them at 1021+, the originals (and some new) are 1001+ +const int AI_FLAG_UDE_HEARTBEAT_EVENT = 0x00000001; // 1001 +const int EVENT_HEARTBEAT_EVENT = 1001; +const int AI_FLAG_UDE_HEARTBEAT_PRE_EVENT = 0x00000002; // 1021 +const int EVENT_HEARTBEAT_PRE_EVENT = 1021; + +const int AI_FLAG_UDE_PERCIEVE_EVENT = 0x00000004; // 1002 +const int EVENT_PERCIEVE_EVENT = 1002; +const int AI_FLAG_UDE_PERCIEVE_PRE_EVENT = 0x00000008; // 1022 +const int EVENT_PERCIEVE_PRE_EVENT = 1022; + +const int AI_FLAG_UDE_END_COMBAT_ROUND_EVENT = 0x00000010; // 1003 +const int EVENT_END_COMBAT_ROUND_EVENT = 1003; +const int AI_FLAG_UDE_END_COMBAT_ROUND_PRE_EVENT = 0x00000020; // 1023 +const int EVENT_END_COMBAT_ROUND_PRE_EVENT = 1023; + +const int AI_FLAG_UDE_ON_DIALOGUE_EVENT = 0x00000040; // 1004 +const int EVENT_ON_DIALOGUE_EVENT = 1004; +const int AI_FLAG_UDE_ON_DIALOGUE_PRE_EVENT = 0x00000080; // 1024 +const int EVENT_ON_DIALOGUE_PRE_EVENT = 1024; + +const int AI_FLAG_UDE_ATTACK_EVENT = 0x00000010; // 1005 +const int EVENT_ATTACK_EVENT = 1005; +const int AI_FLAG_UDE_ATTACK_PRE_EVENT = 0x00000020; // 1025 +const int EVENT_ATTACK_PRE_EVENT = 1025; + +const int AI_FLAG_UDE_DAMAGED_EVENT = 0x00000040; // 1006 +const int EVENT_DAMAGED_EVENT = 1006; +const int AI_FLAG_UDE_DAMAGED_PRE_EVENT = 0x00000080; // 1026 +const int EVENT_DAMAGED_PRE_EVENT = 1026; + +const int AI_FLAG_UDE_DEATH_EVENT = 0x00000100; // 1007 +const int EVENT_DEATH_EVENT = 1007; +const int AI_FLAG_UDE_DEATH_PRE_EVENT = 0x00000200; // 1027 +const int EVENT_DEATH_PRE_EVENT = 1027; + +const int AI_FLAG_UDE_DISTURBED_EVENT = 0x00000400; // 1008 +const int EVENT_DISTURBED_EVENT = 1008; +const int AI_FLAG_UDE_DISTURBED_PRE_EVENT = 0x00000800; // 1028 +const int EVENT_DISTURBED_PRE_EVENT = 1028; + +const int AI_FLAG_UDE_RESTED_EVENT = 0x00004000; // 1009 +const int EVENT_RESTED_EVENT = 1009; +const int AI_FLAG_UDE_RESTED_PRE_EVENT = 0x00004000; // 1029 +const int EVENT_RESTED_PRE_EVENT = 1029; + +const int AI_FLAG_UDE_SPELL_CAST_AT_EVENT = 0x00001000; // 1011 +const int EVENT_SPELL_CAST_AT_EVENT = 1011; +const int AI_FLAG_UDE_SPELL_CAST_AT_PRE_EVENT = 0x00002000; // 1031 +const int EVENT_SPELL_CAST_AT_PRE_EVENT = 1031; + +const int AI_FLAG_UDE_COMBAT_ACTION_EVENT = 0x00004000; // 1012 +const int EVENT_COMBAT_ACTION_EVENT = 1012; +const int AI_FLAG_UDE_COMBAT_ACTION_PRE_EVENT = 0x00004000; // 1032 +const int EVENT_COMBAT_ACTION_PRE_EVENT = 1032; + +const int AI_FLAG_UDE_DAMAGED_AT_1_HP = 0x80000000; // 1014 +const int EVENT_DAMAGED_AT_1_HP = 1014; + +const int AI_FLAG_UDE_ON_BLOCKED_EVENT = 0x00008000; // 1015 +const int EVENT_ON_BLOCKED_EVENT = 1015; +const int AI_FLAG_UDE_ON_BLOCKED_PRE_EVENT = 0x00010000; // 1035 +const int EVENT_ON_BLOCKED_PRE_EVENT = 1035; + + +/******************************************************************************/ +/******************************************************************************/ +// Fleeing & Targeting settings, under AI_TARGETING_FLEE_MASTER +/******************************************************************************/ +const string AI_TARGETING_FLEE_MASTER = "AI_TARGETING_FLEE_MASTER"; +/******************************************************************************/ +/******************************************************************************/ + +// Targeting settings +// We only attack the lowest current HP. +const int AI_FLAG_TARGETING_LIKE_LOWER_HP = 0x00000001; +// We only attack the lowest AC (as in 1.2). +const int AI_FLAG_TARGETING_LIKE_LOWER_AC = 0x00000002; +// We go straight for mages/sorcerors +const int AI_FLAG_TARGETING_LIKE_MAGE_CLASSES = 0x00000004; +// We go for those who are using ranged weapons, especially against us! +// 1. Any ranged attackers attacking us. 2. Any ranged attackers. +const int AI_FLAG_TARGETING_LIKE_ARCHERS = 0x00000008; +// Attack PC's we see +const int AI_FLAG_TARGETING_LIKE_PCS = 0x00000010; +// Only attack PC targets if any in range +const int AI_FLAG_TARGETING_FILTER_FOR_PC_TARGETS = 0x00000020; +// Got for lowest HD +const int AI_FLAG_TARGETING_LIKE_LOWER_HD = 0x00000040; + +// Morale settings + +// This will flee to a waypoint set below. +// This is good for fleeing to a boss, fleeing to another area and so on. +const int AI_FLAG_FLEEING_FLEE_TO_WAYPOINT_IF_VALID = 0x00000080; +// This will make the creature never flee at all. +const int AI_FLAG_FLEEING_FEARLESS = 0x00000100; +// This will flee to an object, with the correct tag. +const int AI_FLAG_FLEEING_FLEE_TO_OBJECT = 0x00000200; +// This will make the creature never flee at all. +const int AI_FLAG_FLEEING_FLEE_TO_NEAREST_NONE_SEEN = 0x00000400; +// This will make the creature never fight against impossible odds. +const int AI_FLAG_FLEEING_NEVER_FIGHT_IMPOSSIBLE_ODDS = 0x00000800; +// This turns OFF any sort of group morale bonuses. +const int AI_FLAG_FLEEING_TURN_OFF_GROUP_MORALE = 0x00001000; +// Stops the overriding of HP% we'd need to test our morale. +const int AI_FLAG_FLEEING_NO_OVERRIDING_HP_AMOUNT = 0x00002000; + +// Stored integer, base morale save (Default 10) +const string BASE_MORALE_SAVE = "BASE_MORALE_SAVE";// S.INTEGER + +// Penalites for large damage... +const string AI_DAMAGE_AT_ONCE_FOR_MORALE_PENALTY = "AI_DAMAGE_AT_ONCE_FOR_MORALE_PENALTY";// S.INTEGER +const string AI_DAMAGE_AT_ONCE_PENALTY = "AI_DAMAGE_AT_ONCE_PENALTY";// S.INTEGER + +const string AMOUNT_OF_HD_DIFFERENCE_TO_CHECK = "AMOUNT_OF_HD_DIFFERENCE_TO_CHECK";// S.INTEGER +const string HP_PERCENT_TO_CHECK_AT = "HP_PERCENT_TO_CHECK_AT"; // S.INTEGER +// This is DIFFERENT to the AI_FLEE_TO. This is a set object On Spawn +const string AI_FLEE_OBJECT = "AI_FLEE_OBJECT"; // S.OBJECT + +const string AI_FAVOURED_ENEMY_RACE = "AI_FAVOURED_ENEMY_RACE"; // S.CONSTANT +const string AI_FAVOURED_ENEMY_CLASS = "AI_FAVOURED_ENEMY_CLASS"; // S.CONSTANT + +// How many rounds can we target 1 target? +const string AI_MAX_TURNS_TO_ATTACK_ONE_TARGET = "AI_MAX_TURNS_TO_ATTACK_ONE_TARGET"; + +// Locals recording the progress of re-targetting each target, against the above +// limit, default of 6 rounds. +const string AI_MELEE_TURNS_ATTACKING = "AI_MELEE_TURNS_ATTACKING"; +const string AI_SPELL_TURNS_ATTACKING = "AI_SPELL_TURNS_ATTACKING"; +const string AI_RANGED_TURNS_ATTACKING = "AI_RANGED_TURNS_ATTACKING"; + +// %'s. Chance to change targets in the next round. +const string AI_MELEE_LAST_TO_NEW_TARGET_CHANCE = "AI_MELEE_NT_CHANCE"; +const string AI_RANGED_LAST_TO_NEW_TARGET_CHANCE = "AI_RANGE_NT_CHANCE"; +const string AI_SPELL_LAST_TO_NEW_TARGET_CHANCE = "AI_SPELLS_NT_CHANCE"; + +// Internal AI use only! + +// Targeting often-used constants. +const string MAXIMUM = "MAXIMUM"; +const string MINIMUM = "MINIMUM"; +const int TARGET_HIGHER = 1; +const int TARGET_LOWER = 0; + +const string TARGETING_ISPC = "TARGETING_ISPC"; +const string TARGETING_MANTALS = "TARGETING_MANTALS"; +const string TARGETING_RANGE = "TARGETING_RANGE"; +const string TARGETING_AC = "TARGETING_AC"; +const string TARGETING_SAVES = "TARGETING_SAVES"; +const string TARGETING_PHISICALS = "TARGETING_PHISICALS"; +const string TARGETING_BAB = "TARGETING_BAB"; +const string TARGETING_HITDICE = "TARGETING_HITDICE"; +const string TARGETING_HP_PERCENT = "TARGETING_HP_PERCENT"; +const string TARGETING_HP_CURRENT = "TARGETING_HP_CURRENT"; +const string TARGETING_HP_MAXIMUM = "TARGETING_HP_MAXIMUM"; + +// Generic Include constants for locals +const string AI_LAST_MELEE_TARGET = "AI_LAST_MELEE_TARGET"; +const string AI_LAST_SPELL_TARGET = "AI_LAST_SPELL_TARGET"; +const string AI_LAST_RANGED_TARGET = "AI_LAST_RANGED_TARGET"; +// This is how many turns we've been attacking target X +// Should be 0 to reset targets. +const string GLOBAL_TURNS_ATTACKING_MELEE_TARGET = "GLOBAL_TURNS_ATTACKING_MELEE_TARGET";//S.INTEGER +const string GLOBAL_TURNS_ATTACKING_SPELL_TARGET = "GLOBAL_TURNS_ATTACKING_SPELL_TARGET";//S.INTEGER +const string GLOBAL_TURNS_ATTACKING_RANGED_TARGET = "GLOBAL_TURNS_ATTACKING_RANGED_TARGET";//S.INTEGER + +/******************************************************************************/ +/******************************************************************************/ +// Fighting & Spells variables, set under AI_COMBAT_MASTER +/******************************************************************************/ +const string AI_COMBAT_MASTER = "AI_COMBAT_MASTER"; +// AI_FLAG_COMBAT_ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +// Mage settings +/******************************************************************************/ +// This will make the creature counterspell mages specifically +const int AI_FLAG_COMBAT_COUNTER_SPELL_ARCANE = 0x00000001; +// This will make the creature counterspell clerics specifically. +const int AI_FLAG_COMBAT_COUNTER_SPELL_DIVINE = 0x00000002; +// This will make the creature only counterspell if it has a group to +// help it. +const int AI_FLAG_COMBAT_COUNTER_SPELL_ONLY_IN_GROUP = 0x00000004; + +// Makes sure that any targeting never hits allies. +const int AI_FLAG_COMBAT_NEVER_HIT_ALLIES = 0x00000010; +// This ignores allies that would survive by being hit by the spell +const int AI_FLAG_COMBAT_AOE_DONT_MIND_IF_THEY_SURVIVE = 0x00000020; +// Targets more with single target (rather than AOE) spells, trying +// to kill one target rather than many. +const int AI_FLAG_COMBAT_SINGLE_TARGETING = 0x00000040; +// Likes AOE spells - more the merrier, for targeting. +const int AI_FLAG_COMBAT_MANY_TARGETING = 0x00000080; +// Improved ... +const int AI_FLAG_COMBAT_IMPROVED_INSTANT_DEATH_SPELLS = 0x00002000; +const int AI_FLAG_COMBAT_IMPROVED_SUMMON_TARGETING = 0x00004000; +const int AI_FLAG_COMBAT_IMPROVED_IMMUNITY_CHECKING = 0x00008000; +const int AI_FLAG_COMBAT_IMPROVED_SPECIFIC_SPELL_IMMUNITY= 0x00010000; +// When set, this will force use all potions +const int AI_FLAG_COMBAT_USE_ALL_POTIONS = 0x00020000; +// This will make the creature use ranged spells, before moving in bit by bit. +// Ranges of spells are 40, then 20, 8, 2.5 and then 0 (or self! hehe) +const int AI_FLAG_COMBAT_LONGER_RANGED_SPELLS_FIRST = 0x00040000; +// Dispels highest level mage in range rather then spell target. +const int AI_FLAG_COMBAT_DISPEL_MAGES_MORE = 0x00080000; +// Dispels in order of spell power on dispel target +const int AI_FLAG_COMBAT_DISPEL_IN_ORDER = 0x00100000; +// Turn on more ally buffing +const int AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS = 0x00200000; + +// This will make the creature summon thier respective familiars/animal companions. +const int AI_FLAG_COMBAT_SUMMON_FAMILIAR = 0x00400000; +// Uses spells, to buff before an enemy comes near. (changed: not NW_prefix) +const int AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY = 0x00800000; + +// Set integers + +const string AI_AOE_HD_DIFFERENCE = "AI_AOE_HD_DIFFERENCE"; +// Ignore AOE target if got this amount of allies in a blast area +const string AI_AOE_ALLIES_LOWEST_IN_AOE = "AI_AOE_ALLIES_LOWEST_IN_AOE"; + +// Cheat cast spells set to this (SetAIConstant, with a + IntToString(1+) added) +const string AI_CHEAT_CAST_SPELL = "AI_CHEAT_CAST_SPELL"; + +// the spell last valid random casting +const string GLOBAL_LAST_SPELL_INFORMATION = "GLOBAL_LAST_SPELL_INFORMATION"; + +/******************************************************************************/ +// Fighter settings +/******************************************************************************/ +// This sets to pick up weapons which are disarmed. +const int AI_FLAG_COMBAT_PICK_UP_DISARMED_WEAPONS = 0x01000000; +// IE TANK! Well, if we do have ranged weapons (EG giants with slings +// for "rocks"), but are bloody tougher in HTH. +const int AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND = 0x02000000; +// Moves back, uses more ranged weapons +const int AI_FLAG_COMBAT_ARCHER_ATTACKING = 0x04000000; +// Forces move back if got ranged weapon +const int AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK = 0x08000000; +// Forces using a bow (if got one) +const int AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW = 0x10000000; +// No killing off low HP PC's +const int AI_FLAG_COMBAT_NO_GO_FOR_THE_KILL = 0x20000000; + +// What range to use missile weapons +const string AI_RANGED_WEAPON_RANGE = "AI_RANGED_WEAPON_RANGE"; +/******************************************************************************/ +// Dragon settings +/******************************************************************************/ +// No wing buffet. +const int AI_FLAG_COMBAT_NO_WING_BUFFET = 0x40000000; +// (Dragon) flying on +const int AI_FLAG_COMBAT_FLYING = 0x80000000; + +// String constants - integers +const string AI_DRAGON_FREQUENCY_OF_BUFFET = "AI_DRAGON_FREQUENCY_OF_BUFFET"; +const string AI_DRAGON_FREQUENCY_OF_BREATH = "AI_DRAGON_FREQUENCY_OF_BREATH"; +// Constants for counters +const string AI_DRAGONS_BREATH = "AI_DRAGONS_BREATH"; +const string AI_WING_BUFFET = "AI_WING_BUFFET"; + +// Flying to target +const string AI_FLYING_TARGET = "AI_FLYING_TARGET"; + +/******************************************************************************/ +/******************************************************************************/ +// Other Combat - Healing, Skills & bosses - AI_OTHER_COMBAT_MASTER +/******************************************************************************/ +const string AI_OTHER_COMBAT_MASTER = "AI_OTHER_COMBAT_MASTER"; +//AI_FLAG_OTHER_COMBAT_ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +// Healing settings +// % to heal allies. Advanced healing search, and if they are a front liner or not. +/******************************************************************************/ +const int AI_FLAG_OTHER_COMBAT_HEAL_AT_PERCENT_NOT_AMOUNT = 0x00000001; +const int AI_FLAG_OTHER_COMBAT_WILL_RAISE_ALLIES_IN_BATTLE = 0x00000002; +const int AI_FLAG_OTHER_COMBAT_NO_CURING = 0x00000004; +const int AI_FLAG_OTHER_COMBAT_ONLY_CURE_SELF = 0x00000008; +const int AI_FLAG_OTHER_COMBAT_ONLY_RESTORE_SELF = 0x00000010; +const int AI_FLAG_OTHER_COMBAT_GIVE_POTIONS_TO_HELP = 0x00000020; +const int AI_FLAG_OTHER_COMBAT_USE_BAD_HEALING_SPELLS = 0x00000040; + +// Locals +const string AI_HEALING_US_PERCENT = "AI_HEALING_US_PERCENT"; // S.INTEGER +const string AI_HEALING_ALLIES_PERCENT = "AI_HEALING_ALLIES_PERCENT"; // S.INTEGER +const string SECONDS_BETWEEN_STATUS_CHECKS = "ROUNDS_BETWEEN_STATUS_CHECKS";// S.INTEGER + +/******************************************************************************/ +// Skill settings +// How the NPC uses skills (pickpocket, taunt, healing kits...) and disarming traps. +/******************************************************************************/ +const int AI_FLAG_OTHER_COMBAT_NO_PICKPOCKETING = 0x00000080; +const int AI_FLAG_OTHER_COMBAT_FORCE_PICKPOCKETING = 0x00000100; +const int AI_FLAG_OTHER_COMBAT_NO_TAUNTING = 0x00000200; +const int AI_FLAG_OTHER_COMBAT_FORCE_TAUNTING = 0x00000400; +const int AI_FLAG_OTHER_COMBAT_NO_EMPATHY = 0x00000800; +const int AI_FLAG_OTHER_COMBAT_FORCE_EMPATHY = 0x00001000; +const int AI_FLAG_OTHER_COMBAT_NO_HIDING = 0x00002000; +const int AI_FLAG_OTHER_COMBAT_FORCE_HIDING = 0x00004000; +const int AI_FLAG_OTHER_COMBAT_NO_OPENING_LOCKED_DOORS = 0x00008000; +const int AI_FLAG_OTHER_COMBAT_FORCE_OPENING_LOCKED_DOORS = 0x00010000; +const int AI_FLAG_OTHER_COMBAT_NO_USING_HEALING_KITS = 0x00020000; +const int AI_FLAG_OTHER_COMBAT_FORCE_USING_HEALING_KITS = 0x00040000; +const int AI_FLAG_OTHER_COMBAT_NO_SEARCH = 0x00080000; +const int AI_FLAG_OTHER_COMBAT_FORCE_SEARCH = 0x00100000; +const int AI_FLAG_OTHER_COMBAT_NO_CONCENTRATION = 0x00200000; +const int AI_FLAG_OTHER_COMBAT_FORCE_CONCENTRATION = 0x00400000; +const int AI_FLAG_OTHER_COMBAT_NO_PARRYING = 0x00800000; +const int AI_FLAG_OTHER_COMBAT_FORCE_PARRYING = 0x01000000; + +// Counts up the concentration move counter, if over 5 we won't move back AGAIN +// but stand and fight a bit! +const string AI_CONCENTRATIONMOVE_COUNTER = "AI_CONCENTRATIONMOVE_COUNTER"; + +/******************************************************************************/ +// Boss settings, AI_BOSS_MASTER +// Leaders/Boss settings +/******************************************************************************/ +// Boss shout. Brings creatures in a pre-set, or standard range to us. +const int AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT = 0x02000000; +// Are we the group leader? +const int AI_FLAG_OTHER_COMBAT_GROUP_LEADER = 0x04000000; + +// Range for the shout +const string AI_BOSS_MONSTER_SHOUT_RANGE = "AI_BOSS_MONSTER_SHOUT_RANGE"; +// counter - adds 1, when 4+, we may shout for people to attack our target. +const string AI_LEADER_SHOUT_COUNT = "AI_LEADER_SHOUT_COUNT"; + +/******************************************************************************/ +/******************************************************************************/ +// Bioware variables +// NW_GENERIC_MASTER. Mostly used for animations + stuff. +/******************************************************************************/ +const string NW_GENERIC_MASTER = "NW_GENERIC_MASTER"; +/******************************************************************************/ +/******************************************************************************/ + +//const int NW_FLAG_SPECIAL_CONVERSATION = 0x00000001; +//const int NW_FLAG_SHOUT_ATTACK_MY_TARGET = 0x00000002; +const int NW_FLAG_STEALTH = 0x00000004; +const int NW_FLAG_SEARCH = 0x00000008; +//const int NW_FLAG_SET_WARNINGS = 0x00000010; +//const int NW_FLAG_ESCAPE_RETURN = 0x00000020; //Failed +//const int NW_FLAG_ESCAPE_LEAVE = 0x00000040; +//const int NW_FLAG_TELEPORT_RETURN = 0x00000080; //Failed +//const int NW_FLAG_TELEPORT_LEAVE = 0x00000100; +//const int NW_FLAG_PERCIEVE_EVENT = 0x00000200; +//const int NW_FLAG_ATTACK_EVENT = 0x00000400; +//const int NW_FLAG_DAMAGED_EVENT = 0x00000800; +//const int NW_FLAG_SPELL_CAST_AT_EVENT = 0x00001000; +//const int NW_FLAG_DISTURBED_EVENT = 0x00002000; +//const int NW_FLAG_END_COMBAT_ROUND_EVENT = 0x00004000; +//const int NW_FLAG_ON_DIALOGUE_EVENT = 0x00008000; +//const int NW_FLAG_RESTED_EVENT = 0x00010000; +//const int NW_FLAG_DEATH_EVENT = 0x00020000; +//const int NW_FLAG_SPECIAL_COMBAT_CONVERSATION = 0x00040000; +const int NW_FLAG_AMBIENT_ANIMATIONS = 0x00080000; +//const int NW_FLAG_HEARTBEAT_EVENT = 0x00100000; +const int NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS = 0x00200000; +const int NW_FLAG_DAY_NIGHT_POSTING = 0x00400000; +const int NW_FLAG_AMBIENT_ANIMATIONS_AVIAN = 0x00800000; +//const int NW_FLAG_APPEAR_SPAWN_IN_ANIMATION = 0x01000000; +//const int NW_FLAG_SLEEPING_AT_NIGHT = 0x02000000; +//const int NW_FLAG_FAST_BUFF_ENEMY = 0x04000000; + + +/******************************************************************************/ +// Other settings, AI_OTHER_MASTER +// Death, minor things. +/******************************************************************************/ +const string AI_OTHER_MASTER = "AI_OTHER_MASTER"; +//AI_FLAG_OTHER_ +/******************************************************************************/ + +/******************************************************************************/ +// Death +/******************************************************************************/ +// This turns off corpses - they won't be set to destroyable, and will be +// destroyed on death, instantly. +const int AI_FLAG_OTHER_TURN_OFF_CORPSES = 0x00000001; +// This will turn on Bioware's SetLootable stuff. It is performed On Death (Using EffectRessurection) +const int AI_FLAG_OTHER_USE_BIOWARE_LOOTING = 0x00400000; + +const string AI_WE_WILL_CREATE_ON_DEATH = "AI_WE_WILL_CREATE_ON_DEATH";// S.STRING +const string AI_DEATH_VISUAL_EFFECT = "AI_DEATH_VISUAL_EFFECT"; // S.CONSTANT +const string AI_CORPSE_DESTROY_TIME = "AI_CORPSE_DESTROY_TIME"; // S.INTEGER + +/******************************************************************************/ +// Behaviour +/******************************************************************************/ +// We search around going nearer enemies on heartbeat. +const int AI_FLAG_OTHER_SEARCH_IF_ENEMIES_NEAR = 0x00000002; +// We change to hostile when we are attacked. +const int AI_FLAG_OTHER_CHANGE_FACTIONS_TO_HOSTILE_ON_ATTACK = 0x00000004; +// We don't attack when we see an enemy, they must attack us normally. +const int AI_FLAG_OTHER_ONLY_ATTACK_IF_ATTACKED = 0x00000008; +// We rest at the end of combat, after searching. +const int AI_FLAG_OTHER_REST_AFTER_COMBAT = 0x00000010; +// No voice chats. +const int AI_FLAG_OTHER_NO_PLAYING_VOICE_CHAT = 0x00000040; + +const string AI_SEARCH_IF_ENEMIES_NEAR_RANGE = "AI_SEARCH_IF_ENEMIES_NEAR_RANGE";// S.INTEGER +const string AI_DOOR_INTELLIGENCE = "AI_DOOR_INTELLIGENCE"; // S.INTEGER +// Items and loot picking extra bags. +const string AI_LOOT_BAG_OTHER_1 = "AI_LOOT_BAG_OTHER_1"; // S.STRING +const string AI_LOOT_BAG_OTHER_2 = "AI_LOOT_BAG_OTHER_2"; // S.STRING +const string AI_LOOT_BAG_OTHER_3 = "AI_LOOT_BAG_OTHER_3"; // S.STRING + +/******************************************************************************/ +// Alignment - only 2 things +/******************************************************************************/ +// Do we stop commoner default? +const int AI_FLAG_OTHER_NO_COMMONER_ALIGNMENT_CHANGE = 0x00000080; +// Do we stop commoner default? +const int AI_FLAG_OTHER_FORCE_COMMONER_ALIGNMENT_CHANGE = 0x00000100; + +/******************************************************************************/ +// Lag settings +/******************************************************************************/ +// We do not use any items. +const int AI_FLAG_OTHER_LAG_NO_ITEMS = 0x00000200; +// No casting of spells! +const int AI_FLAG_OTHER_LAG_NO_SPELLS = 0x00000400; +// No listening, for anything. +const int AI_FLAG_OTHER_LAG_NO_LISTENING = 0x00000800; +// Use ActionEquipMostDamagingMelee/Ranged instead of my set weapons. +const int AI_FLAG_OTHER_LAG_EQUIP_MOST_DAMAGING = 0x00001000; +// We never cure allies - so less GetHasEffect checks. +const int AI_FLAG_OTHER_LAG_NO_CURING_ALLIES = 0x00002000; +// return; on heartbeat. +const int AI_FLAG_OTHER_LAG_IGNORE_HEARTBEAT = 0x00004000; +// Go for the nearest seen enemy always +const int AI_FLAG_OTHER_LAG_TARGET_NEAREST_ENEMY = 0x00008000; + +// SetAI level things. +const string LAG_AI_LEVEL_NO_PC_OR_ENEMY_50M = "LAG_AI_LEVEL_NO_PC_OR_ENEMY_50M"; +const string LAG_AI_LEVEL_YES_PC_OR_ENEMY_50M = "LAG_AI_LEVEL_YES_PC_OR_ENEMY_50M"; +const string LAG_AI_LEVEL_COMBAT = "LAG_AI_LEVEL_COMBAT"; + +/******************************************************************************/ +// Other settings +/******************************************************************************/ + +// If they are damaged a lot, they may spawn a critical wounds potion and use it. +const int AI_FLAG_OTHER_CHEAT_MORE_POTIONS = 0x00010000; +// This will store thier starting location, and then move back there after combat +// Will turn off if there are waypoints. +const int AI_FLAG_OTHER_RETURN_TO_SPAWN_LOCATION = 0x00020000; +// This will affect conversations - will they clear all actions before hand? +const int AI_FLAG_OTHER_NO_CLEAR_ACTIONS_BEFORE_CONVERSATION = 0x00040000; +// Last one! This stops all polymorphing by spells/feats +const int AI_FLAG_OTHER_NO_POLYMORPHING = 0x00080000; +// Ignore emotes +const int AI_FLAG_OTHER_DONT_RESPOND_TO_EMOTES = 0x00100000; +// Don't ever shout +const int AI_FLAG_OTHER_DONT_SHOUT = 0x00200000; + +const string EMOTE_STAR = "*"; +// Locals +const string AI_POLYMORPH_INTO = "AI_POLYMORPH_INTO"; // S.CONSTANT +const string AI_RETURN_TO_POINT = "AI_RETURN_TO_POINT"; // S.INTEGER + +/******************************************************************************/ +// The spell validnessess: +/******************************************************************************/ +const string AI_VALID_SPELLS = "AI_VALID_SPELLS"; +/******************************************************************************/ +// Max CR used OnSpawn or other GetCreatureTalentBest places +const int MAXCR = 20; + +// Doesn't set what they are, only if they exsist. +const int AI_VALID_TALENT_HARMFUL_AREAEFFECT_DISCRIMINANT = 0x00000001;// 1 - This is the constant number added to AI_VALID_SPELLS spawn settings. +const int AI_VALID_TALENT_HARMFUL_RANGED = 0x00000002;// 2 +const int AI_VALID_TALENT_HARMFUL_TOUCH = 0x00000004; +const int AI_VALID_TALENT_BENEFICIAL_HEALING_AREAEFFECT = 0x00000008; +const int AI_VALID_TALENT_BENEFICIAL_HEALING_TOUCH = 0x00000010; +const int AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_AREAEFFECT = 0x00000020; +const int AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_SINGLE = 0x00000040; +const int AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_AREAEFFECT = 0x00000080; +const int AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SINGLE = 0x00000100; +const int AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SELF = 0x00000200; +const int AI_VALID_TALENT_HARMFUL_AREAEFFECT_INDISCRIMINANT = 0x00000400; +const int AI_VALID_TALENT_BENEFICIAL_PROTECTION_SELF = 0x00000800; +const int AI_VALID_TALENT_BENEFICIAL_PROTECTION_SINGLE = 0x00001000; +const int AI_VALID_TALENT_BENEFICIAL_PROTECTION_AREAEFFECT = 0x00002000; +const int AI_VALID_TALENT_BENEFICIAL_OBTAIN_ALLIES = 0x00004000; +const int AI_VALID_TALENT_PERSISTENT_AREA_OF_EFFECT = 0x00008000; +const int AI_VALID_TALENT_BENEFICIAL_HEALING_POTION = 0x00010000; +const int AI_VALID_TALENT_DRAGONS_BREATH = 0x00040000; +const int AI_VALID_TALENT_HARMFUL_MELEE = 0x00200000; +// Seperate GetHasSpell checks. Restoration ETC. +const int AI_VALID_CURE_CONDITION_SPELLS = 0x00400000; +// OTHER spells not from the aboves +const int AI_VALID_OTHER_SPELL = 0x00800000; +// Any spell from the aboves +const int AI_VALID_ANY_SPELL = 0x80000000; + +// Unused. This is mearly to not use talents if we don't have any! +//const string AI_TALENT_HARMFUL_MELEE = "AI_TALENT_HM"; + +/******************************************************************************/ +// Other file constants (default1, 2, 4-9, a, b, e +/******************************************************************************/ +// Name of the tempoary target that we have seen/been damaged by/etc. +const string AI_TEMP_SET_TARGET = "AI_TEMP_SET_TARGET"; + +const string AI_TIMER = "AI_TIMER_"; // Prefix for timers +const string AI_CONSTANT = "AI_CONSTANT_"; // Prefix for constansts +const string AI_INTEGER = "AI_INTEGER_"; // Prefix for integers +const string AI_OBJECT = "AI_OBJECT_"; // Prefix for objects + +// Array constants +const string MAXINT_ = "MAXINT_"; +const string s1 = "1"; +const string s0 = "0";// Used mainly for spell triggers + +const string ARRAY_TEMP_ENEMIES = "ARRAY_TEMP_ENEMIES"; +const string ARRAY_TEMP_ALLIES = "ARRAY_TEMP_ALLIES"; +const string ARRAY_TEMP_ARRAY = "ARRAY_TEMP_ARRAY"; + +const string ARRAY_MELEE_ENEMY = "ARRAY_MELEE_ENEMY"; +const string ARRAY_RANGED_ENEMY = "ARRAY_RANGED_ENEMY"; +const string ARRAY_SPELL_ENEMY = "ARRAY_SPELL_ENEMY"; + +// Some generic AI file constants +const string ARRAY_ENEMY_RANGE = "ARRAY_ENEMY_RANGE"; +const string ARRAY_ENEMY_RANGE_SEEN = "ARRAY_ENEMY_RANGE_SEEN"; +const string ARRAY_ENEMY_RANGE_HEARD = "ARRAY_ENEMY_RANGE_HEARD"; + +const string ARRAY_ALLIES_RANGE = "ARRAY_ALLIES_RANGE"; +const string ARRAY_ALLIES_RANGE_SEEN = "ARRAY_ALLIES_RANGE_SEEN"; +const string ARRAY_ALLIES_RANGE_SEEN_BUFF = "ARRAY_ALLIES_RANGE_SEEN_BUFF"; + +// RE-setting weapons uses this local integer identifier. +const string AI_WEAPONSETTING_SETWEAPONS = "AI_WEAPONSETTING_SETWEAPONS"; + +/******************************************************************************/ +// Functions in almost all scripts which include this :-P +/******************************************************************************/ + +// This reports the AI toggle of oTarget. +// * If the AI is OFF, it is TRUE. +// * Returns Dead + Commandable + AI Off integers +int GetAIOff(object oTarget = OBJECT_SELF); +// Sets the AI to NOT use any scripts (or run through them) EXCEPT death! +void SetAIOff(object oTarget = OBJECT_SELF); +// Resets the AI off integer, so the AI scripts run. +void SetAIOn(object oTarget = OBJECT_SELF); + +// This sets a spawn in condition. +// * nCondition - the condition to check for (From the "j_inc_constants" file) +// * sName - The name its stored under +void SetSpawnInCondition(int nCondition, string sName); +// This removes a spawn in condition. +// * nCondition - the condition to check for (From the "j_inc_constants" file) +// * sName - The name its stored under +void DeleteSpawnInCondition(int nCondition, string sName); +// Gets a spawn in condition. +// * nCondition - the condition to check for (From the "j_inc_constants" file) +// * sName - The name its stored under +// * oTarget - The target to look at (Ususally ourselves) +int GetSpawnInCondition(int nCondition, string sName, object oTarget = OBJECT_SELF); + +// We can only ever set ONE special action. These are special things, such as +// fleeing (that are a mixture of special things). +// * Use the AI_SPECIAL_ACTIONS_ constants in the constant file. +void SetCurrentAction(int nAction); +// Deletes the special action stored. +// * Use the AI_SPECIAL_ACTIONS_ constants in the constant file. +void ResetCurrentAction(); +// Gets the current special action stored. +// * Use the AI_SPECIAL_ACTIONS_ constants in the constant file. +int GetCurrentSetAction(); + +// Sets a local constant to sName, adding one to set right (so values of 0 become 1). +// * Use GetLocalConstant to return original value set. +// (To stop local's going awary, we set them with pre-fixes.) +void SetAIConstant(string sName, int iConstant); +// Returns a constant set to sName (Takes one away). +// * Therefore, returns -1 on error. +// (To stop local's going awary, we set them with pre-fixes.) +int GetAIConstant(string sName); +// Deletes a constant set to sName. +// (To stop local's going awary, we set them with pre-fixes.) +void DeleteAIConstant(string sName); + +// Sets a local AI integers to ourselves. +// (To stop local's going awary, we set them with pre-fixes.) +void SetAIInteger(string sName, int iValue); +// Gets a local AI integers from ourselves. +// (To stop local's going awary, we set them with pre-fixes.) +int GetAIInteger(string sName); +// Gets a local AI integers from ourselves. +// - We can define boundries for what it returns. +// (To stop local's going awary, we set them with pre-fixes.) +// If X is < iBottom or > iTop, return iDefault. +int GetBoundriedAIInteger(string sName, int iDefault = 10, int iTop = 10, int iBottom = 1); +// Deletes a local AI integers from ourselves. +// (To stop local's going awary, we set them with pre-fixes.) +void DeleteAIInteger(string sName); + +// Sets a local AI object to ourselves. +// (To stop local's going awary, we set them with pre-fixes.) +void SetAIObject(string sName, object oObject); +// Gets a local AI object from ourselves. +// (To stop local's going awary, we set them with pre-fixes.) +object GetAIObject(string sName); +// Deletes a local AI object from ourselves. +// (To stop local's going awary, we set them with pre-fixes.) +void DeleteAIObject(string sName); + +// Sets up a timer. +// * sName - the variable name (Adds a pre-fix). +// * fDuration - the time until it is removed. +void SetLocalTimer(string sName, float fDuration); +// TRUE if the timer set to sName is still running. +int GetLocalTimer(string sName); + +// Arrays +// Sets a local INTEGER array on ourselves. +// * sArray - the array name. +// * oObjectArray - The object we will set. +// * iValue - The value to check. It is done HIGHEST to LOWEST. +void SetArrayIntegerValue(string sArray, object oObjectArray, int iValue); +// This will move all integer values from a point back a position +// * sArray - the array name. +// * iNumberStart - The value to start at. +// * iMax - The old-highest (or highest in the order) of the array (EG the 10th of 10) +void MoveArrayIntegerBackOne(string sArray, int iNumberStart, int iMax); + +// Sets a local FLOAT array on ourselves. +// * sArray - the array name. +// * oObjectArray - The object we will set. +// * fValue - The value to check. It is done LOWEST (nearest) to HIGHEST (fathest). +void SetArrayFloatValue(string sArray, object oObjectArray, float fValue); +// This will move all float values from a point back a position +// * sArray - the array name. +// * iNumberStart - The value to start at. +// * iMax - The old-highest (or highest in the order) of the array (EG the 10th of 10) +void MoveArrayFloatBackOne(string sArray, int iNumberStart, int iMax); + +// Deletes all the things in an array...set to sArray +void DeleteArray(string sArray); + +// Set the oTarget to ignorable. +// * The AI ignores, and shouldn't intentioally target, the creature. +void SetIgnore(object oTarget); +// Gets if the oTarget is ignorable. +// * The AI ignores, and shouldn't intentioally target, the creature. +int GetIgnore(object oTarget); +// This gets if the oTarget can be targeted as an enemy. +// * Returns if a DM, is faction Equal, is dead or the ignore variable. +int GetIgnoreNoFriend(object oTarget); + +// Fires a User Defined Event. +// * iSpawnValue - The spawn value (like NW_FLAG_PERCIEVE_PRE_EVENT) +// * iNumber - The number to fire (like EVENT_PERCIEVE_PRE_EVENT) +// Returns TRUE if the event fires. +int FireUserEvent(int iSpawnValue, int iNumber); +// This sets to exit the script. Use in the defaultd (On User Defined) file. +// For example: We want to not attack PC's with the item "ROCK" (Tag). We +// therefore use the event EVENT_PERCIEVE_PRE_EVENT to exit if they have that item +// because we go friendly to them. +// * iNumber - The user defined number to exit from. +void SetToExitFromUDE(int iNumber); +// This is used for Pre-events. If we exit from EVENT_PERCIEVE_PRE_EVENT, and +// use SetToExitFromUDE, this returns TRUE (ONCE!) +// * iNumber - The user defined number to exit from. +int ExitFromUDE(int iNumber); + +// We check if we are attacking anything +// * Checks Attempted* Targets, Get the AttackTarget of us. +// * Checks GetIsInCombat at the end. If that is TRUE, we should be doing proper rounds anyway. +int GetIsFighting(); +// We check if we can perform a combat action currently. +// * Checks our action list. Some things like skills, opening doors and so on +// we don't want to interrupt before they are done. +// * Also used within DetermineCombatRound +int GetIsBusyWithAction(); +// This checks GetIsFighting and GetIsBusyWithAction, returns FALSE if we can +// do a combat round (IE we don't have anything to interrupt and are not already +// in combat!) +// * IE it adds GetIsBusyWithAction with GetIsFighting to give 0, 1 or 2. +// * Checks if we are fleeing too +int CannotPerformCombatRound(); +// This will SpeakString a value from sName's array. i1000 uses a d1000 for % chance +void SpeakArrayString(string sName, int i1000 = FALSE); +// This is used in combat (at the end thereof) and when something shouts, and is a placeable. +// * oTarget - The target which may have shouted, or similar. Moves to and closes normally. +// If no oTarget, it still searches +void Search(object oTarget = OBJECT_INVALID); +// Used in Search(). This apply Trueseeing, See invisibility, or Invisiblity purge +// if we have neither of the 3 on us. +void SearchSpells(); + +// Returns our custom AI file (if any) +// - Blank string if not set +string GetCustomAIFileName(); +// Sets our custom AI file +// - Needs a 16 or less character string +// - Should execute actions +// - Can sort actions against a imputted target (EG: On Percieved enemy) by +// "GetLocalObject(OBJECT_SELF, AI_TEMP_SET_TARGET)" +void SetCustomAIFileName(string sAIFileName); + +// This is still used - we just set a local object and execute script. +// Set the script to use by COMBAT_FILE constant +void DetermineCombatRound(object oTarget = OBJECT_INVALID); + +// This checks the current special action (fleeing, runner, door smashing) +// - Returns FALSE if none of them are performed +// Use this to make sure that an ActionMoveTo or DCR doesn't fire if we are fleeing. +// - This does not perform any actions. +int GetIsPerformingSpecialAction(); + +// This will, if we are set that we can, shout the string. +void AISpeakString(string sString); + +/*:://///////////////////////////////////////////// +//:: Name: AI ON, or OFF. +//:://///////////////////////////////////////////// + Scripted version of AI on/off toggle. + + It stops all AI scripts from running - using "if(GetAIStatus()) return;" + + Simple, but effective! +//::////////////////////////////////////////////*/ +int GetAIOff(object oTarget) +{ + if(GetIsDead(oTarget) || + !GetCommandable(oTarget) || + GetLocalInt(oTarget, AI_TOGGLE)) + { + return TRUE; + } + return FALSE; +} +void SetAIOff(object oTarget) +{ + SetLocalInt(oTarget, AI_TOGGLE, TRUE); +} +void SetAIOn(object oTarget) +{ + DeleteLocalInt(oTarget, AI_TOGGLE); +} +/*:://///////////////////////////////////////////// +//:: Name: GetSpawnInCondition +//:://///////////////////////////////////////////// + This returns the condition of it (True or False) + You can specify a name (as some use AI_SKILLS_MASTER for example.) + 1.3: Changed to a simpler thing, hopefully faster (taken from SoU AI). +//::////////////////////////////////////////////*/ +int GetSpawnInCondition(int nCondition, string sName, object oTarget = OBJECT_SELF) +{ + return (GetLocalInt(oTarget, sName) & nCondition); +} +void SetSpawnInCondition(int nCondition, string sName) +{ + SetLocalInt(OBJECT_SELF, sName, (GetLocalInt(OBJECT_SELF, sName) | nCondition)); +} +void DeleteSpawnInCondition(int nCondition, string sName) +{ + SetLocalInt(OBJECT_SELF, sName, (GetLocalInt(OBJECT_SELF, sName) & ~nCondition)); +} +/*:://///////////////////////////////////////////// +//:: Name: SetCurrentAction, ResetCurrentAction, GetCurrentSetAction. +//:://///////////////////////////////////////////// + We can only ever set ONE action. These are special things, such as + fleeing. + Use the AI_ACTION constants in the constant file. +//::////////////////////////////////////////////*/ +void SetCurrentAction(int nAction) +{ + SetLocalInt(OBJECT_SELF, AI_CURRENT_ACTION, nAction); +} +void ResetCurrentAction() +{ + DeleteLocalInt(OBJECT_SELF, AI_CURRENT_ACTION); +} +int GetCurrentSetAction() +{ + return GetLocalInt(OBJECT_SELF, AI_CURRENT_ACTION); +} +/*:://///////////////////////////////////////////// +//:: Name: SetLocalTimer, GetLocalTimer +//:://///////////////////////////////////////////// + Gets/Sets a local timer. +//::////////////////////////////////////////////*/ +void SetLocalTimer(string sName, float fDuration) +{ + sName = AI_TIMER + sName; + SetLocalInt(OBJECT_SELF, sName, TRUE); + DelayCommand(fDuration, DeleteLocalInt(OBJECT_SELF, sName)); +} +int GetLocalTimer(string sName) +{ + return GetLocalInt(OBJECT_SELF, AI_TIMER + sName); +} +/*:://///////////////////////////////////////////// +//:: Name: Array functions. +//:://///////////////////////////////////////////// + Set array values to local integers/floats under the values stated. +//::////////////////////////////////////////////*/ +// Sets a local INTEGER array on ourselves. +// * sArray - the array name. +// * oObjectArray - The object we will set. +// * iValue - The value to check. It is done HIGHEST to LOWEST. +void SetArrayIntegerValue(string sArray, object oObjectArray, int iValue) +{ + int iValueAtPosition, iMax, i; + // Get the current size + iMax = GetLocalInt(OBJECT_SELF, MAXINT_ + sArray); + string sArrayStore = sArray; + // Special - IE no valid array values at the start. + if(iMax < i1) + { + sArrayStore += s1; + SetLocalInt(OBJECT_SELF, sArray + s1, iValue); + SetLocalObject(OBJECT_SELF, sArray + s1, oObjectArray); + SetLocalInt(OBJECT_SELF, MAXINT_ + sArray, i1); // always the first. + } + // Else, we will set it in the array. + else + { + // Loop through the items stored already. + for(i = i1; i <= iMax; i++) + { + // Get the value of the item. + iValueAtPosition = GetLocalInt(OBJECT_SELF, sArray + IntToString(i)); + // If imput is greater than stored...move all of them back one. + if(iValue > iValueAtPosition) + { + // Move all values from this point onwards back one. + MoveArrayIntegerBackOne(sArray, i, iMax); + // Set the local object and the local integer. + sArrayStore += IntToString(i); + SetLocalInt(OBJECT_SELF, sArrayStore, iValue); + SetLocalObject(OBJECT_SELF, sArrayStore, oObjectArray); + // Set max values we have (IE add one!) 1.3beta - changed from iMax++ + SetLocalInt(OBJECT_SELF, MAXINT_ + sArray, ++iMax); + break; + } + // If at end, just add at end. + else if(i == iMax) + { + // Set the local object and the local integer. + sArrayStore += IntToString(i + i1); + SetLocalInt(OBJECT_SELF, sArrayStore, iValue); + SetLocalObject(OBJECT_SELF, sArrayStore, oObjectArray); + // Set max values we have (IE add one!) + SetLocalInt(OBJECT_SELF, MAXINT_ + sArray, ++iMax); + break; + } + } + } +} +// This will move all integer values from a point back a position +// * sArray - the array name. +// * iNumberStart - The value to start at. +// * iMax - The old-highest (or highest in the order) of the array (EG the 10th of 10) +void MoveArrayIntegerBackOne(string sArray, int iNumberStart, int iMax) +{ + // Objects, the old name for that value and the new one. + object oObjectAtNumber; + string sCurrentName, sNewName; + int iArrayAtNumberValue, i; + // Move it from the back, back one, then then next... + // Start at value 5 (Max) and move it to 6, then move to 4, move it + // to 5 and so on down to iNumber Start, say, 3, so we get to 3, move it to 4, + // and space 3 is free. + for(i = iMax; i >= iNumberStart; i--) + { + // Sets the name up right. + sCurrentName = sArray + IntToString(i); // The current name to get values. + sNewName = sArray + IntToString(i + i1); // Move back = Add one + // Set the things up in the right parts. + oObjectAtNumber = GetLocalObject(OBJECT_SELF, sCurrentName); + iArrayAtNumberValue = GetLocalInt(OBJECT_SELF, sCurrentName); + // To the NEW name - we add one to the i value. + SetLocalObject(OBJECT_SELF, sNewName, oObjectAtNumber); + SetLocalInt(OBJECT_SELF, sNewName, iArrayAtNumberValue); + } +} +// Sets a local FLOAT array on ourselves. +// * sArray - the array name. +// * oObjectArray - The object we will set. +// * fValue - The value to check. It is done LOWEST (nearest) to HIGHEST (fathest). +void SetArrayFloatValue(string sArray, object oObjectArray, float fValue) +{ + int iMax, i; + iMax = GetLocalInt(OBJECT_SELF, MAXINT_ + sArray); + string sArrayStore = sArray; + float fValueAtPosition; + // Special - IE no valid array values at the start. + if(iMax <= FALSE) + { + sArrayStore = sArray + s1; + SetLocalFloat(OBJECT_SELF, sArrayStore, fValue); + SetLocalObject(OBJECT_SELF, sArrayStore, oObjectArray); + SetLocalInt(OBJECT_SELF, MAXINT_ + sArray, i1); // always the first. + } + // Else, we will set it in the array. + else + { + // Loop through the items stored already. + for(i = i1; i <= iMax; i++) + { + // Get the value of the item. + fValueAtPosition = GetLocalFloat(OBJECT_SELF, sArray + IntToString(i)); + // If imput is LESS (nearer) than stored...move all of them back one. + if(fValue < fValueAtPosition) + { + // Move all values from this point onwards back one. + MoveArrayFloatBackOne(sArray, i, iMax); + // Set the local object and the local integer. + sArrayStore = sArray + IntToString(i); + SetLocalFloat(OBJECT_SELF, sArrayStore, fValue); + SetLocalObject(OBJECT_SELF, sArrayStore, oObjectArray); + // Set max values we have (IE add one!) 1.3 beta - it was iMax++ + SetLocalInt(OBJECT_SELF, MAXINT_ + sArray, ++iMax); + break; + } + // Else, if it is the end value (iMax) we set it at the end + else if(i == iMax) + { + // Set the local object and the local integer. + sArrayStore = sArray + IntToString(i + i1); + SetLocalFloat(OBJECT_SELF, sArrayStore, fValue); + SetLocalObject(OBJECT_SELF, sArrayStore, oObjectArray); + // Set max values we have (IE add one!) + SetLocalInt(OBJECT_SELF, MAXINT_ + sArray, ++iMax); + break; + } + } + } +} +// This will move all float values from a point back a position +// * sArray - the array name. +// * iNumberStart - The value to start at. +// * iMax - The old-highest (or highest in the order) of the array (EG the 10th of 10) +void MoveArrayFloatBackOne(string sArray, int iNumberStart, int iMax) +{ + // Objects, the old name for that value and the new one. + object oObjectAtNumber; + string sCurrentName, sNewName; + int i; + float fArrayAtNumberValue; + // Move it from the back, back one, then then next... + for(i = iMax; i >= iNumberStart; i--) + { + // Sets the name up right. + sCurrentName = sArray + IntToString(i); // The current name to get values. + sNewName = sArray + IntToString(i + i1); // Move back = Add one + // Set the things up in the right parts. + oObjectAtNumber = GetLocalObject(OBJECT_SELF, sCurrentName); + fArrayAtNumberValue = GetLocalFloat(OBJECT_SELF, sCurrentName); + // To the NEW name - we add one to the i value. + SetLocalObject(OBJECT_SELF, sNewName, oObjectAtNumber); + SetLocalFloat(OBJECT_SELF, sNewName, fArrayAtNumberValue); + } +} +// Deletes all the things in an array...set to sArray +void DeleteArray(string sArray) +{ + int i, iMax; + string sNewName; + // Max values, if any + iMax = GetLocalInt(OBJECT_SELF, MAXINT_ + sArray); + if(iMax) + { + for(i = i1; i <= iMax; i++) + { + sNewName = sArray + IntToString(i); + DeleteLocalObject(OBJECT_SELF, sNewName);// Object + DeleteLocalInt(OBJECT_SELF, sNewName);// Value + DeleteLocalFloat(OBJECT_SELF, sNewName);// Value + } + } + // Here, we do delete the max + DeleteLocalInt(OBJECT_SELF, MAXINT_ + sArray); +} + + +/*:://///////////////////////////////////////////// +//:: Name: SetLocalTimer, GetLocalTimer +//:://///////////////////////////////////////////// + Set/Get the oTarget to ignorable. + * The AI ignores, and shouldn't intentioally target, the creature. +//::////////////////////////////////////////////*/ +void SetIgnore(object oTarget) +{ + SetLocalInt(oTarget, AI_IGNORE_TOGGLE, TRUE); +} +int GetIgnore(object oTarget) +{ + return GetLocalInt(oTarget, AI_IGNORE_TOGGLE); +} +int GetIgnoreNoFriend(object oTarget) +{ + if(GetLocalInt(oTarget, AI_IGNORE_TOGGLE) || GetFactionEqual(oTarget) || + GetIsDM(oTarget) || GetIsDead(oTarget)) + { + return TRUE; + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: SetLocalConstant, GetLocalConstant +//:://///////////////////////////////////////////// + Sets a constant (from nwscript.nss) +//::////////////////////////////////////////////*/ +void SetAIConstant(string sName, int iConstant) +{ + SetLocalInt(OBJECT_SELF, AI_CONSTANT + sName, iConstant + i1); +} +int GetAIConstant(string sName) +{ + return GetLocalInt(OBJECT_SELF, AI_CONSTANT + sName) - i1; +} +void DeleteAIConstant(string sName) +{ + DeleteLocalInt(OBJECT_SELF, AI_CONSTANT + sName); +} +/*:://///////////////////////////////////////////// +//:: Name: SetAIInteger, GetAIInteger +//:://///////////////////////////////////////////// + To stop local's going awary, we set them with pre-fixes. +//::////////////////////////////////////////////*/ +void SetAIInteger(string sName, int iValue) +{ + SetLocalInt(OBJECT_SELF, AI_INTEGER + sName, iValue); +} +int GetAIInteger(string sName) +{ + return GetLocalInt(OBJECT_SELF, AI_INTEGER + sName); +} +int GetBoundriedAIInteger(string sName, int iDefault, int iTop, int iBottom) +{ + int iReturn = GetAIInteger(sName); + // Boundries + if(iReturn < iBottom || iReturn > iTop) + { + iReturn = iDefault; + } + return iReturn; +} +void DeleteAIInteger(string sName) +{ + DeleteLocalInt(OBJECT_SELF, AI_INTEGER + sName); +} +/*:://///////////////////////////////////////////// +//:: Name: SetAIObject, GetAIObject +//:://///////////////////////////////////////////// + To stop local's going awary, we set them with pre-fixes. +//::////////////////////////////////////////////*/ +void SetAIObject(string sName, object oObject) +{ + SetLocalObject(OBJECT_SELF, AI_OBJECT + sName, oObject); +} +object GetAIObject(string sName) +{ + return GetLocalObject(OBJECT_SELF, AI_OBJECT + sName); +} +void DeleteAIObject(string sName) +{ + DeleteLocalObject(OBJECT_SELF, AI_OBJECT + sName); +} + +/*:://///////////////////////////////////////////// +//:: Name: FireUserEvent +//:://///////////////////////////////////////////// +// Fires a User Defined Event. +// * iSpawnValue - The spawn value (like NW_FLAG_PERCIEVE_PRE_EVENT) +// * iNumber - The number to fire (like EVENT_PERCIEVE_PRE_EVENT) +// Returns TRUE if the event fires. +//::////////////////////////////////////////////*/ +int FireUserEvent(int iSpawnValue, int iNumber) +{ + // Check spawn in condition + if(GetSpawnInCondition(iSpawnValue, AI_UDE_MASTER)) + { + // Signal event (and return TRUE) + SignalEvent(OBJECT_SELF, EventUserDefined(iNumber)); + return TRUE; + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: SetToExitFromUDE +//:://///////////////////////////////////////////// +// This sets to exit the script. Use in the defaultd (On User Defined) file. +// For example: We want to not attack PC's with the item "ROCK" (Tag). We +// therefore use the event EVENT_PERCIEVE_PRE_EVENT to exit if they have that item +// because we go friendly to them. +// * iNumber - The user defined number to exit from. +//::////////////////////////////////////////////*/ +void SetToExitFromUDE(int iNumber) +{ + SetLocalInt(OBJECT_SELF, EXIT_UDE_PREFIX_ + IntToString(iNumber), TRUE); +} +/*:://///////////////////////////////////////////// +//:: Name: ExitFromUDE +//:://///////////////////////////////////////////// +// This is used for Pre-events. If we exit from EVENT_PERCIEVE_PRE_EVENT, and +// use SetToExitFromUDE, this returns TRUE (ONCE!) +// * iNumber - The user defined number to exit from. +//::////////////////////////////////////////////*/ +int ExitFromUDE(int iNumber) +{ + // Set up string to delete/check + string sCheck = EXIT_UDE_PREFIX_ + IntToString(iNumber); + // Check local value + if(GetLocalInt(OBJECT_SELF, sCheck)) + { + // Delete if valid (not equal to one) and return TRUE to exit + DeleteLocalInt(OBJECT_SELF, sCheck); + return TRUE; + } + return FALSE; +} + +/*:://///////////////////////////////////////////// +//:: Name: GetIsFighting, GetIsBusyWithAction, CanPerformCombatRound +//:://///////////////////////////////////////////// +// Busy things - are we doing an action we don't want to override with ClearAllActions? +// Checks them here. +//::////////////////////////////////////////////*/ + +// We check if we are attacking anything +// * Checks Attempted* Targets, Get the AttackTarget of us. +// * Checks GetIsInCombat at the end. If that is TRUE, we should be doing proper rounds anyway. +int GetIsFighting() +{ + // Do we already have a target? + if(GetIsObjectValid(GetAttackTarget()) || + GetIsObjectValid(GetAttemptedAttackTarget()) || + GetIsObjectValid(GetAttemptedSpellTarget())) + { + return TRUE; + } + // Final check. Are we in combat? + return GetIsInCombat(); +} +// We check if we can perform a combat action currently. +// * Checks our action list. Some things like skills, opening doors and so on +// we don't want to interrupt before they are done. +// * Also used within DetermineCombatRound +int GetIsBusyWithAction() +{ + // Set up actions. + int iAction = GetCurrentAction(); + // Common dropout ones to speed it up. + if(iAction == ACTION_INVALID || + iAction == ACTION_WAIT || + iAction == ACTION_FOLLOW || + iAction == ACTION_MOVETOPOINT || + iAction == ACTION_DIALOGOBJECT || + iAction == ACTION_REST || + iAction == ACTION_USEOBJECT || + iAction == ACTION_COUNTERSPELL || + iAction == ACTION_DISABLETRAP || + iAction == ACTION_EXAMINETRAP || + iAction == ACTION_FLAGTRAP || + iAction == ACTION_RECOVERTRAP) + { + return FALSE; + } + else + if(iAction == ACTION_ATTACKOBJECT)// Very common. Could be a door as well as a creature! + { + // This is a special thing...if we are attacking a non-creature, we + // return FALSE anyway, to attack the creature. + + // Therefore, if we are attacking a creature though, we return TRUE as + // we do not want to change objects. :-P + int iAttackObjectType = GetObjectType(GetAttackTarget()); + // Note: as this returns -1 on error, its easier to just use an if/else + // checking the integers, with a -1 dropout. + if(iAttackObjectType != -1) + { + // We never stop attacking one creature + if(iAttackObjectType == OBJECT_TYPE_CREATURE) + { + return TRUE; + } + // we may stop attacking a door if we are not fleeing though + // But if we are attacking a door and fleeing, don't react. + else if(iAttackObjectType == OBJECT_TYPE_DOOR && + !GetIsObjectValid(GetAIObject(AI_FLEE_TO))) + { + return TRUE; + } + } + } + // We are opening a door... (or unlocking one) + else + if(iAction == ACTION_OPENDOOR || // Opening a door! + iAction == ACTION_OPENLOCK) // The AI only unlocks doors + { + // It may be that we want to always unlock doors and open them as we + // are fleeing. + if(!GetIsObjectValid(GetAIObject(AI_FLEE_TO))) + { + return TRUE; + } + } + // If we are using a cirtain skill or similar, don't try and attack. + else + if(iAction == ACTION_ANIMALEMPATHY || // An "attack" skill we use + iAction == ACTION_CASTSPELL || // Casting a spell shouldn't be interrupted. + iAction == ACTION_HEAL || // Heal skill. A very important way to heal and not to override + iAction == ACTION_ITEMCASTSPELL || // Scrolls, potions ETC. + iAction == ACTION_LOCK || // Won't be used. Added for completeness. + iAction == ACTION_PICKPOCKET || // Sometimes used in combat. Nifty! + iAction == ACTION_PICKUPITEM || // We may be picking up lost weapons (disarmed ones) + iAction == ACTION_SETTRAP || // Can't seem to work it :-/ Well, here for completeness + iAction == ACTION_TAUNT) // Taunt shouldn't be interrupted. + { + return TRUE; + } + return FALSE; +} +// This checks GetIsFighting and GetIsBusyWithAction, returns FALSE if we can +// do a combat round (IE we don't have anything to interrupt and are not already +// in combat!) +// * IE it adds GetIsBusyWithAction with GetIsFighting to give 0, 1 or 2. +// * Checks if we are fleeing too +int CannotPerformCombatRound() +{ + return GetIsPerformingSpecialAction() + GetIsBusyWithAction() + GetIsFighting(); +} + +// This will SpeakString a value from sName's array. i1000 uses a d1000 for % chance +void SpeakArrayString(string sName, int i1000 = FALSE) +{ + // Need a valid array (arrays of 1 are just that - 1 value to choose from.) + int iSize = GetLocalInt(OBJECT_SELF, ARRAY_SIZE + sName); + if(iSize > i0) + { + // Make sure we are not dead (unless we should be) + if(sName != AI_TALK_ON_DEATH) + { + if(GetIsDead(OBJECT_SELF)) return; + } + // Do we carry on? + int iCarryOn = FALSE; + if(i1000 == TRUE) + { + // Do the % check now. Values 1-1000 randomised. + if((Random(1000) + i1) <= GetLocalInt(OBJECT_SELF, ARRAY_PERCENT + sName)) iCarryOn = TRUE; + } + else + { + // 100 normal one. + if(d100() <= GetLocalInt(OBJECT_SELF, ARRAY_PERCENT + sName)) iCarryOn = TRUE; + } + if(iCarryOn) + { + int iRandomOne = i1; + if(iSize > i1) + { + // Randomise - we add one, so instead of 0-2 (for 3 values) it goes 1-3. + iRandomOne = Random(iSize) + i1; + } + // Now, to choose one... + string sSpeak = GetLocalString(OBJECT_SELF, sName + IntToString(iRandomOne)); + // And speak! + // - Added random delay for 0.1 to 1.2 seconds to add some variety, + // if it is used for n1000 + if(sSpeak != "") + { + // Code sorta taken from NW_I0_Spells, the random stuff. + // FloatToInt(15);/FloatToInt(fRandom * 10.0); + if(i1000 == TRUE) + { + float fDelay = IntToFloat(Random(15) + 1) / 10.0; + DelayCommand(fDelay, SpeakString(sSpeak)); + } + else + { + // Speak instantly. + SpeakString(sSpeak); + } + } + } + } +} +// Used in Search(). This apply Trueseeing, See invisibility, or Invisiblity purge +// if we have neither of the 3 on us. +void SearchSpells() +{ + effect eCheck = GetFirstEffect(OBJECT_SELF); + int iEffectType, iBreak; + while(GetIsEffectValid(eCheck) && iBreak == FALSE) + { + iEffectType = GetEffectType(eCheck); + if(iEffectType == EFFECT_TYPE_TRUESEEING || + iEffectType == EFFECT_TYPE_SEEINVISIBLE) + { + iBreak = TRUE; + } + eCheck = GetNextEffect(OBJECT_SELF); + } + // We have effects, stop. + if(iBreak == TRUE && !GetHasSpellEffect(SPELL_INVISIBILITY_PURGE)) + { + return; + } + // Else we apply the best spell we have. + if(GetHasSpell(SPELL_TRUE_SEEING)) + { + ActionCastSpellAtObject(SPELL_TRUE_SEEING, OBJECT_SELF); + return; + } + if(GetHasSpell(SPELL_SEE_INVISIBILITY)) + { + ActionCastSpellAtObject(SPELL_SEE_INVISIBILITY, OBJECT_SELF); + return; + } + if(GetHasSpell(SPELL_INVISIBILITY_PURGE)) + { + ActionCastSpellAtObject(SPELL_INVISIBILITY_PURGE, OBJECT_SELF); + return; + } +} +// This is used in combat (at the end thereof) and when something shouts, and is a placeable. +// * oTarget - The target which may have shouted, or similar. Moves to and closes normally. +// If no oTarget, it still searches +void Search(object oTarget = OBJECT_INVALID) +{ + if(GetIsObjectValid(GetAttemptedSpellTarget()) || + GetIsObjectValid(GetAttemptedAttackTarget()) || + GetIsObjectValid(GetAttackTarget()) || + GetLocalTimer(AI_TIMER_SEARCH)) + { + return; + } + else + { + // Stop now + ClearAllActions(); + // Rest after combat? + if(GetSpawnInCondition(AI_FLAG_OTHER_REST_AFTER_COMBAT, AI_OTHER_MASTER)) + { + // 71: "[Search] Resting" + DebugActionSpeakByInt(71); + ForceRest(OBJECT_SELF); + ActionWait(f1); + } + // Determine the amount of time to search if there is antone else around. + int iIntelligence = GetBoundriedAIInteger(AI_INTELLIGENCE, i10, i10, i1); + float fTime = IntToFloat(iIntelligence * i3); + // Set local timer for a minimum of 4 seconds + SetLocalTimer(AI_TIMER_SEARCH, f4); + // Check some spells. Cast one if we have no true seeing ETC. + SearchSpells(); + // Stealth/search. + int iStealth = GetStealthMode(OBJECT_SELF); + int iSearch = GetDetectMode(OBJECT_SELF); + // We perfere to hide again if we search if set to...sneaky! + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER)) + { + if(iStealth != STEALTH_MODE_ACTIVATED) + { + SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE); + } + } + else + { + // If we are hiding, stop to search (we shouldn't be - who knows?) + if(iStealth == STEALTH_MODE_ACTIVATED) + { + SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE); + } + // And search! + if(iSearch != DETECT_MODE_ACTIVE && !GetHasFeat(FEAT_KEEN_SENSE)) + { + SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE); + } + } + // We check around the target, if there is one. + if(GetIsObjectValid(oTarget)) + { + ActionMoveToLocation(GetLocation(oTarget)); + // If it is a chest ETC. We close it. + if(GetIsOpen(oTarget)) + { + if(GetObjectType(oTarget) == OBJECT_TYPE_DOOR) + { + ActionCloseDoor(oTarget); + } + else + { + // Close it + ActionDoCommand(DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE)); + } + } + } + // We will get nearest enemy at the very least + else + { + // Use nearest heard + object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD); + if(GetIsObjectValid(oEnemy)) + { + // Move to location + ActionMoveToLocation(GetLocation(oEnemy)); + } + } + // Note: Here, we will return to spawn location after moving to the + // object, if it is a valid setting, else we do the normal randomwalk + if(GetSpawnInCondition(AI_FLAG_OTHER_RETURN_TO_SPAWN_LOCATION, AI_OTHER_MASTER)) + { + ActionMoveToLocation(GetLocalLocation(OBJECT_SELF, AI_RETURN_TO_POINT)); + } + else + { + // 72: "[Search] Searching, No one to attack. [Time] " + FloatToString(fTime, i3, i2) + DebugActionSpeakByInt(72, OBJECT_INVALID, FALSE, FloatToString(fTime, i3, i2)); + // Randomly walk. + ActionRandomWalk(); + // Clear all actions stops Random Walk + DelayCommand((fTime - f1), ClearAllActions()); + // Delay a 30 second walk waypoints (which stops ActionRandomWalk). + DelayCommand(fTime, ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF)); + } + } +} + +// Returns our custom AI file (if any) +// - Blank string if not set +string GetCustomAIFileName() +{ + return GetLocalString(OBJECT_SELF, AI_CUSTOM_AI_SCRIPT); +} +// Sets our custom AI file +// - Needs a 16 or less character string +// - Should execute actions +// - Can sort actions against a imputted target (EG: On Percieved enemy) by +// "GetLocalObject(OBJECT_SELF, AI_TEMP_SET_TARGET)" +void SetCustomAIFileName(string sAIFileName) +{ + SetLocalString(OBJECT_SELF, AI_CUSTOM_AI_SCRIPT, sAIFileName); +} + +/*:://///////////////////////////////////////////// +//:: Name: DetermineCombatRound +//:://///////////////////////////////////////////// + This is still used - we just set a local object and execute script. + Argument - iCustom = Custom Files, so we determine not with default3 +//:://///////////////////////////////////////////*/ +void DetermineCombatRound(object oTarget = OBJECT_INVALID) +{ + // Check for custom AI script, else fire default. + string sAI = GetCustomAIFileName(); + // Fire default AI script + if(sAI == "") + { + // Sanity check - to not fire this off multiple times, we make sure temp + // object is not the same as oTarget (and valid) + if(!GetIsObjectValid(oTarget) || (GetIsObjectValid(oTarget) && + !GetLocalTimer(AI_DEFAULT_AI_COOLDOWN))) + { + // 73: "[Call for DCR] Default AI [Pre-Set Target]" + GetName(oTarget) + DebugActionSpeakByInt(73, oTarget); + SetLocalObject(OBJECT_SELF, AI_TEMP_SET_TARGET, oTarget); + ExecuteScript(COMBAT_FILE, OBJECT_SELF); + SetLocalTimer(AI_DEFAULT_AI_COOLDOWN, 0.1); + } + } + // Fire custom AI script + else + { + SetLocalObject(OBJECT_SELF, AI_TEMP_SET_TARGET, oTarget); + // 74: "[Call for DCR] Custom AI [" + sAI + "] [Pre-Set Target]" + GetName(oTarget) + DebugActionSpeakByInt(74, oTarget, FALSE, sAI); + // Execute it + ExecuteScript(sAI, OBJECT_SELF); + } +} + + +// This checks the current special action (fleeing, runner, door smashing) +// - Returns FALSE if none of them are performed +// Use this to make sure that an ActionMoveTo or DCR doesn't fire if we are fleeing. +int GetIsPerformingSpecialAction() +{ + int iAction = GetCurrentSetAction(); + object oTarget = GetAttackTarget(); + object oRunTarget; + switch(iAction) + { + case AI_SPECIAL_ACTIONS_ME_RUNNER: + { + oRunTarget = GetAIObject(AI_RUNNER_TARGET); + if(GetIsObjectValid(oRunTarget)) + { + if(GetObjectSeen(oRunTarget)) + { + // Stop thinking we are a runner if we can see the run target + ResetCurrentAction(); + } + else + { + // Else we are running to the + return TRUE; + } + } + } + break; + case AI_SPECIAL_ACTIONS_FLEE: + { + oRunTarget = GetAIObject(AI_FLEE_TO); + if(GetIsObjectValid(oRunTarget)) + { + if(GetObjectSeen(oRunTarget)) + { + // If we see the flee target, reset targets + ResetCurrentAction(); + } + else + { + // Else flee! + return TRUE; + } + } + else + { + // Check if we have bad intellgence, and we will run away + // from the nearest enemy if heard. + if(GetAIInteger(AI_INTELLIGENCE) <= i3) + { + oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oRunTarget)) + { + oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oRunTarget)) + { + oRunTarget = GetLastHostileActor(); + if(!GetIsObjectValid(oRunTarget) || GetIsDead(oRunTarget)) + { + ResetCurrentAction(); + return FALSE; + } + } + } + // Running from enemy + return TRUE; + } + ResetCurrentAction(); + } + } + break; + case AI_SPECIAL_ACTIONS_MOVE_OUT_OF_AOE: + { + // We must be X distance away from a cirtain AOE, if we are not, we + // move. + oRunTarget = GetAIObject(AI_AOE_FLEE_FROM); + + // If not valid, or already far enough away, delete special action + // and return false. + if(!GetIsObjectValid(oRunTarget) || + GetLocalFloat(OBJECT_SELF, AI_AOE_FLEE_FROM_RANGE) < GetDistanceToObject(oRunTarget)) + { + ResetCurrentAction(); + } + else + { + // Valid and still in range + return TRUE; + } + } + break; + } + // Return false to carry on a normal DCR or move to enemy. + return FALSE; +} + +// This will, if we are set that we can, shout the string. +void AISpeakString(string sString) +{ + // Spawn condition to turn it off. + if(!GetSpawnInCondition(AI_FLAG_OTHER_DONT_SHOUT, AI_OTHER_MASTER)) + { + // Silent talk = "DebugMode 1" only can see, is the "talk" version of the DM + // channel. + SpeakString(sString, TALKVOLUME_SILENT_TALK); + } +} +// Debug: To compile this script full, uncomment all of the below. +/* - Add two "/"'s at the start of this line +void main() +{ + return; +} +//*/ diff --git a/_content/_hak/rune_prc8_top/j_inc_debug.nss b/_content/_hak/rune_prc8_top/j_inc_debug.nss new file mode 100644 index 0000000..802ab9c --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_debug.nss @@ -0,0 +1,188 @@ +/************************ [Debug] ********************************************** + Filename: J_Inc_Debug +************************* [Debug] ********************************************** + This contains DebugActionSpeak, the debug function. + + Makes it easier to uncomment debug lines. +************************* [History] ******************************************** + 1.3 - Added +************************* [Workings] ******************************************* + DebugActionSpeak normally writes a timestamped log entry, and speak a silent + string Server Admins can hear. + + To Do: Might make it more generic debug lines, where you can uncomment all + "XX" lines HERE, not in the files, so it compiles without them, and only + need an integer to speak one. + + 1.3 added: + - DebugActionSpeakByInt(int iInteger); + - Removes many strings into this file + - Can easily comment out all string so they are not added to compiled + scripts if debugging unused (This saves space on compiled files :-D ) + - Always uncomment the right bits if not using any debugging. +************************* [Arguments] ****************************************** + Arguments: N/A +************************* [Debug] *********************************************/ + +// This will speak a cirtain integer number string (similar to a dialog reference). +// - I (Jass) have just moved all strings I used all the time into here, so +// if the strings are uncommented, they will not be compiled +// - The numbers have no reference to much really. +// - Calls DebugActionSpeak! +// - See J_INC_DEBUG to uncomment/recomment in +void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iInput = FALSE, string sInput = ""); + +// Speaks and stamps a debug string. +// - See J_INC_DEBUG to uncomment/recomment the debug strings. +// - Only used in special circumstances. +void DebugActionSpeak(string sString); + +// This will speak a cirtain integer number string (similar to a dialog reference). +// - I (Jass) have just moved all strings I used all the time into here, so +// if the strings are uncommented, they will not be compiled +// - The numbers have no reference to much really. +// - Calls DebugActionSpeak! +// - See J_INC_DEBUG to uncomment/recomment in +void DebugActionSpeakByInt(int iInteger, object oInput = OBJECT_INVALID, int iInput = FALSE, string sInput = "") +{ + // TO UNCOMMENT/COMMENT: + // - Add/Remove in "//" before the next lines "/*" + // - Recompile all files + + /* + + string sDebug; + switch(iInteger) + { + // - Generic AI stuff + case 1: sDebug = "[DCR:Melee] Most Damaging Weapon. Target: " + GetName(oInput); break; + case 2: sDebug = "[DCR:Melee] Most Damaging as Not Effective"; break; + case 3: sDebug = "[DCR:Melee] Melee Code. No valid melee target/Dead. Exiting"; break; + case 4: sDebug = "[DCR:Melee] Melee attack. [Target] " + GetName(oInput) + " [Feat/Attack] " + IntToString(iInput); break; + case 5: sDebug = "[DCR:Caster] Defensive Casting Mode ON [Enemy] " + GetName(oInput); break; + case 6: sDebug = "[DCR:Caster] Moving away from AOO's. [Enemy] " + GetName(oInput); break; + case 7: sDebug = "[DCR:Casting] Talent(item) [TalentID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break; + case 8: sDebug = "[DCR:Casting] Workaround for Spontaeous [SpellID] " + IntToString(iInput) + " [Target] " + GetName(oInput); break; + case 9: sDebug = "[DCR:Casting] NormalSpell [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break; + case 10: sDebug = "[DCR:Casting] TalentSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break; + case 11: sDebug = "[DCR:Casting] SubSpecialSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break; + case 12: sDebug = "[DCR:Casting] NormalRandomSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break; + case 13: sDebug = "[DCR:Casting] Backup spell caught: " + IntToString(iInput); break; + case 14: sDebug = "[DCR:Feat] [ID] " + IntToString(iInput) + " [Enemy] " + GetName(oInput); break; + case 15: sDebug = "[DCR:Casting] Grenade [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break; + case 16: sDebug = "[AOE Call] Moving out of/Dispeling an AOE. [Tag] " + GetTag(oInput); break; + case 17: sDebug = "[DCR:Special] Darkness + Caster. No seen enemy. Dispel/Move."; break; + case 18: sDebug = "[DRC:Talent] Using Talent (Healing). [TalentID] " + IntToString(iInput) + " [Target] " + GetName(oInput); break; + case 19: sDebug = "[DCR:Healing] (Should) Healing [Target]" + GetName(oInput) + " [CurrentHP|Max|ID|Rank|Power] " + IntToString(iInput); break; + case 20: sDebug = "[DCR Healing] Boss Action, create Critical Wounds potion"; break; + case 21: sDebug = "[DCR:Casting] Healing self with healing kit, [Kit] " + GetName(oInput); break; + case 22: sDebug = "[DCR:Feat] Summoning my familiar"; break; + case 23: sDebug = "[DCR:Feat] Summoning my animal companion"; break; + case 24: sDebug = "[DCR:Fleeing] Stupid/Panic/Flee moving from enemies/position - We are a commoner/no morale/failed < 3 int"; break; + case 25: sDebug = "[DCR:Fleeing] Fleeing to allies. [ID Array] " + sInput + " [Ally] " + GetName(oInput); break; + case 26: sDebug = "[DCR:GFTK] Attacking a PC who is dying/asleep! [Enemy]" + GetName(oInput); break; + case 27: sDebug = "[DCR:Moving] Archer Retreating back from the enemy [Enemy]" + GetName(oInput); break; + case 28: sDebug = "[DCR:Turning] Using Turn Undead"; break; + case 29: sDebug = "[DCR:Bard Song] Using"; break; + case 30: sDebug = "[DCR:Bard Curse Song] Using"; break; + case 31: sDebug = "[DCR:All Spells] Error! No casting (No spells, items, target Etc)."; break; + case 32: sDebug = "[DCR:All Spells] [Modifier|BaseDC|SRA] " + IntToString(iInput); break; + case 33: sDebug = "[DCR:Casting] Cheat Spell. End of Spells. [Spell] " + IntToString(iInput) + "[Target]" + GetName(oInput); break; + case 34: sDebug = "[DCR:All Spells] Ranged Spells. Should use closer spells/move nearer"; break; + case 35: sDebug = "[DCR:Dragon] Breath weapon & attacking [Breath ID] " + IntToString(iInput) + " [Target] " + GetName(oInput); break; + case 36: sDebug = "[DCR:Dragon] Wing Buffet [Target] " + GetName(oInput); break; + case 37: sDebug = "[DCR:Beholder] Teleport"; break; + case 38: sDebug = "[DCR:Beholder] Rays"; break; + case 39: sDebug = "[DCR:Targeting] No valid enemies in sight, moving to allies target's. [Target] " + GetName(oInput); break; + case 40: sDebug = "[DCR:Targeting] Override Target Seen. [Name]" + GetName(oInput); break; + case 41: sDebug = "[DCR:Targeting] No seen in LOS, Attempting to MOVE to something [Target]" + GetName(oInput); break; + case 42: sDebug = "[DCR:Skill] Using agressive skill (+Attack). [Skill] " + IntToString(iInput) + " [Enemy]" + GetName(oInput); break; + case 43: sDebug = "[DCR:Pre-Melee Spells] All Potions Using. [Spell ID] " + IntToString(iInput); break; + case 44: sDebug = "[DCR:Pre-Melee Spells] True Strike Emptive attack [Target] " + GetName(oInput); break; + case 45: sDebug = "[DCR:CounterSpell] Counterspelling. [Target] " + GetName(oInput); break; + case 46: sDebug = "[DRC] START [Intruder]" + GetName(oInput); break; + case 47: sDebug = "[DCR] [PREMITURE EXIT] Cannot Do Anything."; break; + case 48: sDebug = "[DCR] [PREMITURE EXIT] Dazed move away."; break; + case 49: sDebug = "[DCR] [PREMITURE EXIT] Fleeing or otherwise"; break; + case 50: sDebug = "[DRC] END - DELETE PAST TARGETS"; break; + // Perception + case 51: sDebug = "[Perception] Our Enemy Target changed areas. Stopping, moving too...and attack... [Percieved] " + GetName(oInput); break; + case 52: sDebug = "[Perception] Enemy Vanished (Same area) Retargeting/Searching [Percieved] " + GetName(oInput); break; + case 53: sDebug = "[Perception] Enemy seen, and was old enemy/cannot see current. Re-evaluating (no spell) [Percieved] " + GetName(oInput); break; + case 54: sDebug = "[Perception] Enemy Seen. Not in combat, attacking. [Percieved] " + GetName(oInput); break; + case 55: sDebug = "[Perception] Percieved Dead Friend! Moving and Searching [Percieved] " + GetName(oInput); break; + case 56: sDebug = "[Perception] Percieved Alive Fighting Friend! Moving to and attacking. [Percieved] " + GetName(oInput); break; + // Conversation + case 57: sDebug = "[Shout] Friend (may be PC) in combat. Attacking! [Friend] " + GetName(oInput); break; + case 58: sDebug = "[Shout] Responding to shout [Enemy] " + GetName(oInput) + " Who has spoken!"; break; + // Phisical Attacked + case 59: sDebug = "[Phisically Attacked] Attacking back. [Attacker(enemy)] " + GetName(oInput); break; + case 60: sDebug = "[Phisically Attacked] Not same area. [Attacker(enemy)] " + GetName(oInput); break; + // Damaged + case 61: sDebug = "[Damaged] Morale Penalty for 600 seconds [Penalty]" + IntToString(iInput); break; + case 62: sDebug = "[Damaged] Not in combat: DCR [Damager]" + GetName(oInput); break; + case 63: sDebug = "[Damaged] Not in combat: DCR. Ally hit us. [Damager(Ally?)]" + GetName(oInput); break; + // Death + case 64: sDebug = "[Death] Checking corpse status in " + IntToString(iInput) + " [Killer] " + GetName(oInput) + " [Times Died Now] " + sInput; break; + // Disturbed + case 65: sDebug = "[Disturbed] (pickpocket) Attacking Enemy [Disturber] " + GetName(oInput) + " [Type] " + IntToString(iInput); break; + // Rest + case 66: sDebug = "[Rested] Resting. [Type(should be invalid)] " + IntToString(iInput); break; + // Spell Cast at + case 67: sDebug = "[Spell] Caster isn't a creature! May look for target [Caster] " + GetName(oInput); break; + case 68: sDebug = "[Spell:Enemy/Hostile] Not in combat. Attacking: [Caster] " + GetName(oInput); break; + case 69: sDebug = "[Spell] (ally). Not in combat. May Attack/Move [Caster] " + GetName(oInput); break; + // Spell Other AI + // - Shouts + case 70: sDebug = "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput); break; + // Constants + // - Search + case 71: sDebug = "[Search] Resting"; break; + case 72: sDebug = "[Search] Searching, No one to attack. [Time] " + sInput; break; + // - DCR + case 73: sDebug = "[Call for DCR] Default AI [Pre-Set Target]" + GetName(oInput); break; + case 74: sDebug = "[Call for DCR] Custom AI [" + sInput + "] [Pre-Set Target]" + GetName(oInput); break; + // Destroy self + case 75: sDebug = "[Dead] Setting to selectable/destroyable (so we go) for Bioware corpses."; break; + case 76: sDebug = "[Dead] Destroying self finally."; break; + // Waypoints + case 77: sDebug = "[Waypoints] Returning to spawn location. [Area] " + GetName(oInput); break; + + default: return; break; + } + if(sDebug != "") + { + DebugActionSpeak(sDebug); + } + // */ +} + +void DebugActionSpeak(string sString) +{ +// You MUST uncomment this line, IF you use either of the below things + //string sNew = "[Debug]" + GetName(OBJECT_SELF) + "[ObjectID]" + ObjectToString(OBJECT_SELF) + " [Debug] " + sString; + +// Note, uncomment this, so that DM's can hear the debug speaks, normally it is +// only server admins who can hear the debug. If you are not testing, it might +// be best to keep this uncommented. +// Futher: - Must have debug mode set to 1 +// - Only the server admin can seem to see this. + //SpeakString(sNew, TALKVOLUME_SILENT_TALK); + +// Note, uncomment this line to send a message to the first PC in the module. +// - Useful for singleplayer testing + //SendMessageToPC(GetFirstPC(), sNew); + +// This writes the entry to the log, very important, if debugging +// Futher: - If left up for a long time, logs can get very big with the AI +// - Use to find problems in the AI and report to me :-D (Jasperre) + //WriteTimestampedLogEntry(sNew); +} + +// Debug: To compile this script full, uncomment all of the below. +/* +void main() +{ + DebugActionSpeak("Test"); +} +*/ diff --git a/_content/_hak/rune_prc8_top/j_inc_generic_ai.nss b/_content/_hak/rune_prc8_top/j_inc_generic_ai.nss new file mode 100644 index 0000000..a9b261a --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_generic_ai.nss @@ -0,0 +1,14964 @@ +/************************ [Combat Include File] ******************************** + Filename: J_INC_GENERIC_AI +************************* [Combat Include File] ******************************** + This is included in j_ai_detercombat, and executed via the main call + AI_DetermineCombatRound, to determine a hostile action to do against the enemy. + + It works through a "List" of things, until it finds an appropriate action + to undertake. + + It basically runs through: + + - Should I move out of an AOE spell? + - Special abilities like Auras + - Fleeing + - Spells + - Feats (For combat, like Bulls Strength) + - Melee attack (Ranged and Melee) +************************* [History] ******************************************** + 1.3 - All functions prefixed to AI_ + - Lot more documentation + - All parts changed, mainly: + - Better targeting + - Different Effect Setting + - More randomised spellcasting +************************* [Workings] ******************************************* + This is included in "j_ai_detercombat". It is the main combat include file + and determines what to do by checking a lot of conditions. + + It has 2 User-defined Events: + EVENT_COMBAT_ACTION_EVENT = 1012; + EVENT_COMBAT_ACTION_PRE_EVENT = 1032; + + Useful to check special things, and can be used to override default AI + actions, or to change them once they have happened. See the user defined + file for workings of the Pre events. +************************* [Arguments] ****************************************** + Arguments: Global* Objects/Integers/Toggles, GetSpawnInCondition, + GetCommandable, AI_ActionCast* AI_ActionUse* + (Other AI_Action* things) ClearAllActions, GetAC, + GetCurrentHitPoints, GetMaxHitPoints, + GetHitDice, GetCurrentAction (and lots more...). +************************* [Combat Include File] *******************************/ + +/************************ [Intelligence Documentation] ************************* + Intelligence + + in·tel·li·gence ( P ) Pronunciation Key (n-tl-jns) + n. + + 1. a, The capacity to acquire and apply knowledge. + b, The faculty of thought and reason. + c, Superior powers of mind. + 2. An intelligent, incorporeal being, especially an angel. + 3. Information; news. + + 4. a, Secret information, especially about an actual or potential enemy. + b, An agency, staff, or office employed in gathering such information. + c, Espionage agents, organizations, and activities considered as a + group: “Intelligence is nothing if not an institutionalized black + market in perishable commodities” (John le Carré). + + We'll take 1. a, as our definition of intelligence in my AI files. + + It basically has an underlaying effect on how creatures work - it will + affect some of the AI's choices and decisions. + + It will not affect everything. It will never affect anything you can + specifically set in the spawn file, except in rare circumstances. It does + have no effect on: Targeting, Counterspelling, Shouting, Seeing/Percieving. + + - + + + + + +************************* [Intelligence Documentation] ************************/ + +// Include: All constants. Also contains Get/Set/Delete Spawn In Conditions. +#include "j_inc_constants" +// Sets effects. This doesn't have to be seperate, but I like it seperate. +#include "j_inc_seteffects" + +/******************************************************************************/ +// Combat include, spell effect/immune constants +/******************************************************************************/ +// Spell targets, note, have the least immunities, but these ARE set here... +// The spell target genrally is the best average lowest save, or a specific low +// save if we want to specify (and low SR). +// List (use Global to not conflict with the nwscript.nss!) +// - GlobalTargetImmunitiesHex +const int GlobalImmunityFear = 0x00000001; +const int GlobalImmunityNecromancy = 0x00000002; +const int GlobalImmunityMind = 0x00000004; +const int GlobalImmunityNegativeLevel = 0x00000008; +const int GlobalImmunityEntangle = 0x00000008; +const int GlobalImmunitySleep = 0x00000010; +const int GlobalImmunityPoison = 0x00000020; +const int GlobalImmunityDisease = 0x00000040; +const int GlobalImmunityDomination = 0x00000080; +const int GlobalImmunityStun = 0x00000100; +const int GlobalImmunityCurse = 0x00000200; +const int GlobalImmunityConfusion = 0x00000400; +const int GlobalImmunityBlindDeaf = 0x00000800; +const int GlobalImmunityDeath = 0x00000800; +const int GlobalImmunityNegativeEnergy = 0x00001000; +const int GlobalImmunityMantalProtection = 0x00002000; // Its own immunity, checked sometimes. +const int GlobalImmunityPetrify = 0x00004000; +const int GlobalImmunityFlying = 0x00008000; // Earthquake ETC. +const int GlobalImmunitySlow = 0x00010000; + + /*************************** Globals set-up ******************************** + We set up all spells and categories, as well as all targets we may use (its + much easier than imputting countless entries into function headers). Any + sort of globals that are required are also added here - anything that would + pop up in more than one place, or would change from one or more event. + **************************** Globals set-up *******************************/ + +// OUR GLOBAL CONSTANTS +int GlobalIntelligence, GlobalOurHitDice, GlobalOurSize, GlobalOurAC, + GlobalOurBaseAttackBonus, // Used and set for melee. USes GetBaseAttackBonus, as weapons are not set. + GlobalOurRace, GlobalOurGoodEvil, + GlobalSilenceSoItems, // This is set to TRUE if we are affected with silence + // - meaning no proper spells! ALWAYS off if we have auto-silence. + GlobalInTimeStop,// This is used a LOT to see if we are in time stop + GlobalOurChosenClass, GlobalOurChosenClassLevel, // Our chosen class and level of it. + GlobalSpellAbilityModifier, GlobalSpellBaseSaveDC,// Spell globals - IE save DCs etc. + GlobalSaveStupidity, // This is 10 - Intelligence. Makes Save DC checks different + // for lower casters - they may cast even if the enemy always saves! + GlobalSpellPenetrationRoll, // SR penetration roll. + GlobalOurCurrentHP, GlobalOurMaxHP, GlobalOurPercentHP,// HP globals. + GlobalOurAppearance,// Used normally to stop dragon breaths against same dragons! + SRA, // "Spell range attacking", mage behaviour, used for constant checking. + GlobalWeAreSorcerorBard,// Just is GetLevelByClass(CLASS_TYPE_SORCERER/BARD), + // but a global. With summoning, it won't summon a similar one if the old is at low HP + GlobalCanSummonSimilarLevel,// Checked for when set up. If a level over 0, we may + // Summon another spell monster at a similar level. + // The level is set when we cast a spell of it. + GlobalBestSpontaeousHealingSpell,// This is set to the best spell we could + // Spontaeously cast. Set up as talents below. + GlobalTimeStopArraySize, + GlobalLastSpellValid, // Random spells, if this is set to > 0, it casts it after random chances of others + GlobalRandomCastModifier,// Random casting, the extra % added - 2xIntelligence + GlobalWeAreBuffer; // Are we a spell buffer? AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS + + +// OUR GLOBAL TARGETS + +// Melee is actually ANY target we are attacking (depends on ranges and things). +// If OBJECT_INVALID (default) then we don't do the action! +object GlobalMeleeTarget, // Set from either a ranged or melee target :-) + GlobalRangedTarget, // Ranged target + GlobalSpellTarget, // Spell target + GlobalDispelTarget, // Used for breaching too + GlobalNearestEnemySeen, GlobalNearestEnemyHeard, + GlobalNearestSeenAlly, GlobalNearestAlly, + GlobalNearestLeader, GlobalThisArea, + GlobalBuffAlly, GlobalHealingKit, + /* GlobalPreviousTarget,*/ + // - Global Spell targets + // - One target, but a float for range to. + // Right == Ranged slot, non-shield slot. + // Left == Torch slot, secondary slot, shield slot. + GlobalLeftHandWeapon, GlobalRightHandWeapon; + +// ENEMY/ALLY INTEGERS, VLAIDS ETC. + +// Counting etc...global integers + // Must be TRUE else we think there is no enemies to attack. +int GlobalAnyValidTargetObject, + // Counts of 0 or more - specifically in our line of sight. + GlobalEnemiesIn4Meters, GlobalTotalSeenHeardEnemies, + // Ranged/Melee Attackers. Ranged attackers = Attackers with bows. + GlobalMeleeAttackers, GlobalRangedAttackers, + // Total allies + People. We don't count ourselves. + GlobalTotalPeople, GlobalTotalAllies, + // Average's - BAB, HDs. + GlobalAverageEnemyBAB, GlobalAverageEnemyHD, + GlobalAverageFriendlyHD, + // Melee target things... + GlobalMeleeTargetAC, GlobalMeleeTargetBAB, + // Any VALID objects. GetIsObjectValid. + GlobalValidLeader, GlobalValidAlly, GlobalValidSeenAlly, + GlobalValidNearestSeenEnemy, GlobalValidNearestHeardEnemy, + // Dispel counter. Is 1-5. + GlobalDispelTargetHighestDispel, GlobalDispelTargetHighestBreach, + // Spell target globals. Used for one target, throughout the spells... + // - Saves + GlobalSpellTargetWill, GlobalSpellTargetFort, GlobalSpellTargetReflex, + // - Target HD, HP and race + GlobalSpellTargetHitDice, GlobalSpellTargetCurrentHitPoints, GlobalSpellTargetRace, + // - TRUE if seen can see spell target + GlobalSeenSpell, + // Special Spell things - friendly, hostile for spells (!GetIsReactionType, + // GetIsReactionType etc.). + GlobalFriendlyFireHostile, GlobalFriendlyFireFriendly, + // Spell target immunity hex - this is the only one not set to local integer + // for global effects. + GlobalTargetImmunitiesHex, + // If GlobalSpellTarget is immune to our spells VIA. Spell reistance or + // spell immunity, then hostile SPELL (not abilities) are not used. + // - This is a NUMBER - all <= X will NOT work against the enemy + GlobalNormalSpellsNoEffectLevel; +// RANGES, Ie FLOATS :-) +float GlobalRangeToMeleeTarget, GlobalSpellTargetRange, GlobalRangeToNearestEnemy, + GlobalRangeToAlly, GlobalOurReach, GlobalRangeToFuthestEnemy, GlobalBuffRangeAddon; + +// One location - GlobalSummonLocation - for summon location :-) +location GlobalSummonLocation; + +/* This is the set values of spells that the talent actually is. + How it works (for the above). We set if we have items, or potions. + Then, we check the respective talent category. If the spell stored matches, it is true. + then, we go into a special silence check - we get the talent again. It *should* be the + same, and if so, use it! + + Shows what spells are valid, which categories, to skip some GetHasSpell's + Format: + (Other)ther (Special - Darkness, light and silence)(Aura - auras/spells like it). (Rage = Rage) + (Allies) Obtain Allies - summon creature. + (Con)ditional,(Enh)ancement, (Pro)tection + (Harm)armful. + For (B)- (Self) Self. (SinTar)Single Target, (Are)Area effect, + For (H)- (Breath) Breath, (AreaDis) Discriminate, (AreaInd) Indiscriminate +*/ +int +/*1*/ SpellHostAreaDis, ItemHostAreaDis, +/*2*/ SpellHostRanged, ItemHostRanged, +/*3*/ SpellHostTouch, ItemHostTouch, + +/*6*/ SpellConAre, ItemConAre, +/*7*/ SpellConSinTar, ItemConSinTar, +/*8*/ SpellEnhAre, ItemEnhAre, +/*9*/ SpellEnhSinTar, ItemEnhSinTar, +/*10*/ SpellEnhSelf, ItemEnhSelf, +/*11*/ SpellHostAreaInd, ItemHostAreaInd, +/*12*/ SpellProSelf, ItemProSelf, +/*13*/ SpellProSinTar, ItemProSinTar, +/*14*/ SpellProAre, ItemProAre, +/*15*/ SpellAllies, ItemAllies, +/*16*/ SpellAura, + +/*18*/ PotionCon, +/*19*/ SpellHostBreath, +/*20*/ PotionPro, +/*21*/ PotionEnh, + +/*23*/ SpellOtherSpell, +// Special: validness + SpellAnySpellValid, + // Feats + ValidFeats, +// Do we have items (wands) avalible? Or potions? To check spells, we +// make sure that + GobalOtherItemsValid, GobalPotionsValid; + +talent tPotionCon, tPotionPro, tPotionEnh; // These are general talents, Always set + // because we can use these parrallel to spells. + + /*************************** Functions to order **************************** + This should be empty each patch. If you start adding your own things, adding + them here makes changes easy to know about, if you like. I do, anyway :-P + **************************** Functions to order ***************************/ + + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@ SETUP OF THINGS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// This is a group of local variables that are set to 0 each time here, and +// used each round. This is better than local ints to use. + +//*1*/ SpellHostAreaDis, ItemHostAreaDis, +//*2*/ SpellHostRanged, ItemHostRanged, +//*3*/ SpellHostTouch, ItemHostTouch, +// +//*6*/ SpellConAre, ItemConAre, +//*7*/ SpellConSinTar, ItemConSinTar, +//*8*/ SpellEnhAre, ItemEnhAre, +//*9*/ SpellEnhSinTar, ItemEnhSinTar, +//*10*/ SpellEnhSelf, ItemEnhSelf, +//*11*/ SpellHostAreaInd, ItemHostAreaInd, +//*12*/ SpellProSelf, ItemProSelf, +//*13*/ SpellProSinTar, ItemProSinTar, +//*14*/ SpellProAre, ItemProAre, +//*15*/ SpellAllies, ItemAllies, +//*16*/ SpellAura, +// +//*18*/ PotionCon, +//*19*/ SpellHostBreath, +//*20*/ PotionPro, +//*21*/ PotionEnh, +// +//*23*/ SpellOtherSpell + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@ CORE AI FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +// Main call. It goes though many lines of code to decide what to do in combat +// To lengthy to explain here. Basically, input an object, it should attack +// it, or a better target. Define OnSpawn many behaviours to influence choice. +void AI_DetermineCombatRound(object oIntruder = OBJECT_INVALID); + +// These all have the pre-name AttemptX - as they can return TRUE or FALSE +//***************************** AttemptSpell *********************************** + +// This attempts to cast a spell, running down the lists. +// The only variable is iLowest and iBAB level's, targets are globally set. +// - iLowestSpellLevel - If it is set to more than 1, then all spells of that +// level and lower are just ignored. Used for dragons. Default 0. +// - iBABCheckHighestLevel - We check our BAB to see if attacking them would probably +// be a better thing to do. Default 3, it adds 5DC for each level. +// - iLastCheckedRange - Ranged attacking. Starts at 1, and goes to 4 max. +// - Range: Long 40, Medium: 20, Short: 10, Touch: 2.25 (Game meters). Personal = 0 +// NOTE 1: If GlobalItemsOnly is set, we only check for item talents! +// NOTE 2: It uses the SAME target, but for ranged attacking, we use a float range to check range :-) +int AI_AttemptAllSpells(int iLowestSpellLevel = 0, int iBABCheckHighestLevel = 3, int iLastCheckedRange = 1, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); +// Returns TRUE if we counterspell a counter spell target, only does it +// if we have Dispels, and if set to want to be in a group, we are in one :-) +int AI_AttemptCounterSpell(); +//***************************** AttemptSpell *********************************** + +//***************************** AttemptAttack ********************************** + +// Used to attack. Called from AI_AttemptMeleeAttackWrapper +// We do this after we buff up with potions, and so on. +// 1 - Checks our global ranged/melee targets, range too, equips weapons. +// 2 - Checks for a feat to use (IE ranged, called shot ETC, else knockdown ETC). +// 3 - Else normal attack the target, if no feat. +int AI_EquipAndAttack(); +// Used to attack. Called in determine conbat round. +// Wrappers the function AI_EquipAndAttack(), and therefore debug codes. +int AI_AttemptMeleeAttackWrapper(); +// This will: +// - Get the nearest dying PC and kill them off. +// - Use the lowest HP person, and kill them off. +// - Also will check for anyone with the sleep effect, to try and Coup de Grace. +// Requires high Intelligence, And so on, of course. +int AI_AttemptGoForTheKill(); +// We will, normally, use a breath weapon, and if we want to, wing buffet +// It does cast high level spells, using AttemptSpellAllSpells, with +// level 9 ones more often than level 5-7, and 8 are somewhat used :-) +int AI_AttemptDragonCombat(); +// Either the chosen class is a dragon, or the appearance type is a dragon type, +// we return TRUE. +int AI_GetIsDragon(); + +// Beholders have special rays for eyes. This is used if the setting is set +// on spawn for beholders, or if the appearance is a beholder. +int AI_AttemptBeholderCombat(); +// Beholder teleport attempt. Flees from combat. +int AI_ActionBeholderTeleport(); +// Taken from x2_ai_mflayer. +// Bioware's implimenation of the Mind Suck. +void MindFlayerSuck(object oTarget); +// Illithid Use special attacks. +// This is set on spawn, or by the user on spawn. +int AI_AttemptMindflayerCombat(); +//***************************** AttemptAttack ********************************** + +//****************************** AttemptFeat *********************************** + +// GlobalUseTurning is set if we do have things we can turn (And not already!). +// It uses it here if so. +int AI_AttemptFeatTurning(); +// This will use bard song, or damage with curse song! +int AI_AttemptFeatBardSong(); +// Runs through summoning a familiar - uses the feat if it has it. +// INCLUDES companion as well! Will attack at range with a bow if possible. +int AI_AttemptFeatSummonFamiliar(); +// This uses the best combat feat or spell it can, such as barbarian rage, divine power, +// bulls strength and so on. Spells may depend on melee enemy, or HD, or both, or range of enemy. +// Target is used for race's ect. +int AI_AttemptFeatCombatHostile(); +//****************************** AttemptFeat *********************************** + +//**************************** AttemptSpecial ********************************** + +// This will fire any aura's they have, quickened cast, and added to the top of +// thier action queue. +void AI_ActionAbilityAura(); +// This will check if we do not have iSpellID's effects, have got iSpellID, and +// cheat-instant cast the spell if we do. This means that we can use it unlimited +// times (incase it gets dispelled!) +void AI_AttemptCastAuraSpell(int iSpellID); +// Runs though several basic checks. True, if any are performed +// 1 - Darkness. If so, dispel (inc. Ultravision) or move out (INT>=4) +// 2 - AOE spells. Move away from enemy if got effects (more so if no one near) (INT>=3) +// 3 - If invisible, need to move away from any combations. (INT>=6) +// - Returns TRUE if we did anything that would mean we don't want to do +// another action +int AI_AttemptSpecialChecks(); +// This is a good check against the enemies (highest AC one) Damage against concentration +// Also, based on allies, enemies, and things, may move back (random chance, bigger with +// more bad things, like no stoneskins, no invisibility etc.) +// We will do this more at mid-intelligence, and better checks at most intelligence. +// * Not used on spells which require no requirement (EG: pulses ETC) +int AI_AttemptConcentrationCheck(object oTarget); +// This may make the archer retreat - if they are set to, and have a ranged weapon +// and don't have point blank shot, and has a nearby ally. (INT>=1 if set to does it more if higher). +int AI_AttemptArcherRetreat(); +// Will polymorph Self if not already so. Will return TRUE if it casts best on self. +int AI_AttemptPolyMorph(); +// This will cheat-cast iSpell at oTarget. Note that we will know if we have it +// by checking what appearance we have. +void AI_ActionCastShifterSpell(int iSpell, object oTarget = OBJECT_SELF); +//**************************** AttemptSpecial ********************************** + +//**************************** AttemptSkills *********************************** + +// This will use empathy, taunt, and if set, pickpocketing. Most are random, and +// checks are made.Heal is done in healing functions.Done against best melee target, +// or closest seen/heard. +int AI_AttemptHostileSkills(); +// Uses iSkill on GlobalMeleeTarget +// - Fired from AI_AttemptHostileSkills. +void AI_ActionUseSkillOnMeleeTarget(int iSkill); +//**************************** AttemptSkills *********************************** + +//**************************** AttemptMorale *********************************** + +// Fleeing - set OnSpawn for setup. Uses a WILL save! +// - Bonuses in groups +// - May go get help +// - Won't run on a CR/HD threshold. +// - Leaders help! And all is intelligence based. +// Includes commoners fleeing +int AI_AttemptMoraleFlee(); +// Forces the AI to flee from the nearest enemy, or away from our position at least +void AI_ActionFleeScared(); +//**************************** AttemptMorale *********************************** + +//******************************** Other *************************************** +// Sets a value, 1-5, for what we can Dispel. Also sets a 1-5 value for breach spells. +// The values are NOT, I repeat NOT what the spell level are. Generally, they +// class spell-stopping spells as a higher prioritory to Dispel! +// * 5 - Dispeled before hostile spells are cast at target +// * 4 - Dispeled just before level 7 or so spells. +// * 3 - Dispeled just before level 4 or so spells +// * 2 - Dispeled just before level 2 or so spells. +// * 1 - Lowest prioritory - Dispeled at the end. +// There are NO cantrips included (level 0 spells). +// - Targets GlobalDispelTarget +void AI_SetDispelableEnchantments(); +// Returns a dismissal target - a target with a master, and they +// are a Familiar, Animal companion or summon. +// - Nearest one in 10 M. Seen ones only. +object AI_GetDismissalTarget(); +// This will run through most avalible protections spells. +// - TRUE if we cast any. +// Used when invisible to protect before we break the invisibility. +// - We may cast many on allies too +int AI_ActionCastWhenInvisible(); +// This will loop seen allies in a cirtain distance, and get the first one without +// the spells effects of iSpell1 to iSpell4 (and do not have the spells). +// - Quite expensive loop. Only used if we have the spell (iSpellToUse1+) +// in the first place (no items!) and not the timer which stops it for a few rounds (on iSpellToUse1) +// - TRUE if it casts its any of iSpellToUseX's. +// * It has only a % chance to cast if GlobalWeAreBuffer is not TRUE. +int AI_ActionCastAllyBuffSpell(float fMaxRange, int iPercent, int iSpellToUse1, int iSpellToUse2 = -1, int iSpellToUse3 = -1, int iSpellToUse4 = -1, int iSpellOther1 = -1, int iSpellOther2 = -1); +// This will shout, maybe, some commands to allies. Or just command them! +void AI_ActionLeaderActions(); +// Dispels or moves out of the oAOE. +// - If we have >= iMax AND <= iDamage HP... +// AND under iPercent total HP... +// - We Dispel, or move out of the AOE. Move if friendly. +// - Use local object set to iSpell, which should be the nearest of the spell. +int AI_ActionDispelAOE(int iSpell, int iDamageOnly, float fRange, int iDamage, int iMax, int iPercent); +// Casts the breach range of spells on GlobalDispelTarget. TRUE if any are cast. +int AI_ActionCastBreach(); +// Casts the dispel range of spells on GlobalDispelTarget. TRUE if any are cast. +int AI_ActionCastDispel(); +// Wrappers Premonition, Greater Stoneskin and Stoneskin. +// Includes Shades Stoneskin too. SPELL_SHADES_STONESKIN +// - iLowest - 8 = Prem, 6 = Greater, 4 = Stoneskin +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) +int AI_SpellWrapperPhisicalProtections(int iLowest = 1); +// Wrappers Energy Buffer, Protection From Elements, Resist Elements, Endure elements +// - iLowest - Goes 5, 3, 2, 1. +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) +int AI_SpellWrapperElementalProtections(int iLowest = 1); +// Wrappers Haste and Mass Haste. +// - iLowest - 6 = Mass, 3 = Haste +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) +int AI_SpellWrapperHasteEnchantments(int iLowest = 1); +// Wrappers Shadow Shield, Ethereal Visage and Ghostly Visage. +// Includes Greater Shadow Conjuration Ghostly Visage (SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE +// - iLowest - 7 = Shadow, 6 = Ethereal 2 = Ghostly +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections) +int AI_SpellWrapperVisageProtections(int iLowest = 1); +// Wrappers All Mantals (Greater, Normal, Lesser) (Spell Mantals) +// - iLowest - 9 = Greater, 7 = Normal. 5 = Lesser +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) +int AI_SpellWrapperMantalProtections(int iLowest = 1); +// Wrappers All Globes (Greater, Shadow Conjuration, Minor) +// - iLowest - 6 = Greater, 4 = Shadow/Minor +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasGlobeProtections) +int AI_SpellWrapperGlobeProtections(int iLowest = 1); +// Wrappers All Shields - Elemental Shield, Wounding Whispers +// - iLowest - 4 = Elemental, 3 = Wounding, Acid Sheath, 2 = Death Armor. +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalShieldSpell) +int AI_SpellWrapperShieldProtections(int iLowest = 1); +// Wrappers All Mind resistance spells - Mind blank, Lesser and Clarity. bioware likes 3's... +// - iLowest - 8 = Mind Blank, 5 = Lesser, 2 = Clarity +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMindResistanceProtections) +int AI_SpellWrapperMindResistanceProtections(int iLowest = 1); +// Wrappers All Consealment spells - Improved Invisiblity. Displacement. +// - iLowest - 4 = Improved Invisiblit, 3 = Displacement +// - Checks !AI_GetAIHaveEffect(GlobalEffectInvisible, oTarget) && !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells, oTarget) +int AI_SpellWrapperConsealmentEnhancements(object oTarget, int iLowest = 1); +// Shades darkness, assassin feat, normal spell. +int AI_SpellWrapperDarknessSpells(object oTarget); +// Invisibility spells + feats +int AI_SpellWrapperNormalInvisibilitySpells(); +//******************************** Other *************************************** + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@ HEALING FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +//*************************** AttemptHealing *********************************** +// If our current HP is under the percent given, it will check things to heal itself, and use them. +// Used for animals ETC as well. They just don't use potions. +// This will also check levels, for appropriate healing. Set iMyHD to higher to use lower level spells. +int AI_AttemptHealingSelf(); +// Raises dead, and casts healing spells to heal allies. +// Leaders are checked first. +int AI_AttemptHealingAlly(); +// This will heal oTarget using the best spell it can, even ones it can +// spontaeously cast. +// - Just does best spell. +// - May use spontaneous ones as well. :-) +// - Called from AI_AttemptHealing_Self and AI_AttemptHealing_Ally +int AI_ActionHealObject(object oTarget); +// Heals oTarget using undead negative energy type spells +// More basic then the normal healing routines. +// TRUE if it casts anything. +int AI_ActionHealUndeadObject(object oTarget); +// Uses spells to cure conditions. Needs to be checked fully +// - Uses allies own integers to check thier effects +// - Loops spells (Best => worst) and if we havn't cast it in an amount of time +// we check effects (Us, leader, allies seen) until we find someone (nearest) +// who we can cast it on. +int AI_AttemptCureCondition(); +// Get the nearest seen ally creature with the effect. +// - Checks us first +// - Then checks leader +// - Then loops seen allies within 20M. +// See: AI_AttemptCureCondition +object AI_GetNearestAllyWithEffect(int iEffectHex); +// Returns the nearest ally with iMin effects, and X more based on HD. +// - Checks us first. True if (HD/6) Effects and (iMin - 1) effects +// - Checks leader next. True if (HD/5) Effects and (iMin - 2) effects +// - Checks allies after. True if (HD/4) Effects and (iMin) effects +object AI_GetNearestAllyWithRestoreEffects(int iMin); + +// This will return the best spontaeous healing spell, so: +// - It uses just normal GetHasSpell for the clerical healing spells. +// - It gets set up at the start to the global "GlobalBestSpontaeousHealingSpell" +int AI_GetBestSpontaeousHealingSpell(); + +//*************************** AttemptHealing *********************************** + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@ TARGETING & ACTUAL ACTION FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// Actions are ActionX + +//*************************** ActionsSpells ************************************ + +// Special case - it checks the talent again, in silence (if not already so) and +// uses the item, if it is an equal talent. +// - Will call a Shile Equip +// Returns TRUE if we did find the right talent, and it was cast. +int AI_ActionCastItemEqualTo(object oTarget, int iSpellID, int iLocation); +// This attempts to check the talent TALENT_CATEGORY_HARMFUL_RANGED, 2, which +// holds grenades. Easy checks. +// - TRUE if they fire a grenade. +int AI_AttemptGrenadeThrowing(object oTarget); +// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical): +// 1. If they have the spell normally +// 2. If they have an item with the spell. +// 3. If they have a potion of the right type. +// - We always attack with a bow at ranged, but may attack normally after the spell. +// - If nTalent is 0, we do not cast it unless iRequirement is also 0. +// - If iRequirement is 0, it is considered innate. +// - Imput iItemTalentValue and iPotionTalentValue to check item talents. -1 == No item. +int AI_ActionCastSpell(int iSpellID, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1); +// This is used for INFLICT/CURE spells, as GetHasSpell also can return 1+ for +// any extra castings - like if we had 2 light wounds and 2 blesses, it'd return +// 4. +// This, when cast, removes one of the spell being cast, after cheat casting it. +// - DecrementRemainingSpells will work this way quite well, but no choice in +// what to remove, it is faster then 1.3 beta which modified the spell scripts. +int AI_ActionCastSpontaeousSpell(int iSpellID, int nTalent, object oTarget); +// This will cast the shadow conjuration/protection version of the spell, +// via. Cheat Casting, and Decrement Spell Uses. +// - We always attack with a bow at ranged, but may attack normally after the spell. +// - If nTalent is 0, we do not cast it unless iRequirement is also 0. +// - If iRequirement is 0, it is considered innate. +// - Imput iItemTalentValue to check item talents. +int AI_ActionCastSubSpell(int iSubSpell, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1); +// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical): +// 0. If d100() is <= iRandom. +// 1. If they have the spell normally +// 2. If they have an item with the spell. +// 3. If they have a potion of the right type. +// - If we are at range from nearest enemy, we attack with a ranged weapon, else do nothing more. +// - If nTalent is -1, we do not check items. +// - Sets GlobalLastSpellValid to iSpellID if we can cast it, but don't randomly. +int AI_ActionCastSpellRandom(int iSpellID, int nTalent, int iRandom, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1); +// This will cast the spell/feat iThing, depending if it is a spell or a feat, +// at the summon location chosen before. Works similar to normal spells but +// at a special location. +// * If iRequirement is -1, it is a feat. If 0, it is innate (as spells) +int AI_ActionCastSummonSpell(int iThingID, int iRequirement = 0, int iSummonLevel = 0); +// This willcast iFirst -> iFirst + iAmount polymorph spell. It will check +// if we have iMaster (Either by feat or spell, depending on iFeat). +// TRUE if we polymorph. +// * Only decrements if iRemove is TRUE +int AI_ActionPolymorph(int iMaster, int iFirstSpell, int iAmount, int iFeat = FALSE, int iRemove = TRUE); + +// Used with GlobalLastSpellValid. If GlobalLastSpellValid is 0, sets locals for +// use later, and sets GlobalLastSpellValid to the spell in question. +void AI_SetBackupCastingSpellValues(int iSpellID, int nTalent, object oTarget, int iLocation, int iRequirement, int iItemTalentValue, int iPotionTalentValue); +// This will used a stored GlobalLastSpellValid to see if it should cast that +// spell (or attempt to!) as a backup. Uses stored targets from when it did know +// it was valid. +int AI_ActionCastBackupRandomSpell(); + +// If they have the feat, they will use it on the target, and return TRUE +// * iFeat - Feat ID to use +// * oObject - object to use it on (Can't target locations in the AI - not worth it) +int AI_ActionUseFeatOnObject(int iFeat, object oObject = OBJECT_SELF); +// If they have nFeat, they cheat-cast nSpell at oTarget. +// - This is a workaround, as some epic spells, for some reason, won't work with +// a standard ActionUseFeatOnObject()! Dammit. Beta 3 addition. +int AI_ActionUseEpicSpell(int nFeat, int nSpell, object oTarget = OBJECT_SELF); +// Wrappers action Use Talent with debug string +void AI_ActionUseTalentDebug(talent tChosenTalent, object oTarget); +// Wrapper to check all dragon breaths, against oTarget +int AI_ActionDragonBreath(object oTarget, int iWingCounter); +// Uses tBreath if they are not immune +// - TRUE if used. +int AI_ActionUseBreath(object oTarget, talent tBreath, int iSpellID); + +// Special: Apply Item Start +void AI_SpecialActionApplyItem(); +// Special: Remove the effect (immobilize) +void AI_SpecialActionRemoveItem(); +// Only TRUE if we have a flee object. +// - Uses sArray to debug and speak +int AI_ActionFlee(string sArray); +//*************************** ActionsSpells ************************************ + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@ ALL INFO FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +// Gets a item talent value, applies EffectCutsceneImmobilize then removes it. +// - iTalent, 1-21. +void AI_SetItemTalentValue(int iTalent); +// This checks if nTalent is one that will be checked for by items. +int AI_GetSpellCategoryHasItem(int nTalent); + +// Equips the best shield we have. +// - Used before we cast a spell so casters can gain maximum AC. +void AI_EquipBestShield(); +// Turns on/off all melee things (not stealth or search!) but not iMode. +void AI_SetMeleeMode(int iMode = -1); + +// GETTING ENEMY INFO + +// We set up targets to Global* variables, GlobalSpellTarget, GlobalRangeTarget, +// and GlobalMeleeTarget. Counts enemies, and so on. +// - Uses oIntruder (to attack or move near) if anything. +// - We return TRUE if it ActionAttack's, or moves to an enemy - basically +// that we cannot do an action, but shouldn't search. False if normal. +int AI_SetUpAllObjects(object oImputBackup); +// This sets up US, the user! :-) +// - Determines class to use, dragon or not. +// - And some other things that don't need to check allies/enemies for. +// - Intelligence and so on, global effects of us and so on ;-) +void AI_SetUpUs(); +// Sets up all our effects, ones to heal as well. +void AI_SetUpUsEffects(); +// Using the array ARRAY_ENEMY_RANGE, we return a % of seen/heard people who +// DO have any of the spells which see through the invisiblity spells. +// * iLimit - when we get to this number of people who have invisiblity, we stop and return 100% +// If ANY of the people are attacking us and have the effect, we return +30% for each. +int AI_GetSpellWhoCanSeeInvis(int iLimit); + +// Returns the object to the specifications: +// * fRange - Within fRange (fTouchRange 2.25, fShortRange 8.0, fMediumRange 20.0, fLongRange = 40.0) +// * fSpread - Radius Size - RADIUS_SIZE_* constants (1.67 to 10.0M) +// * nLevel - Used for saving throws/globe checks. Level of the spell added to save checks +// * iSaveType = FALSE - SAVING_THROW_FORT/REFLEX/WILL. Not type, but the main save applied with it. +// * nShape = SHAPE_SPHERE - SHAPE_* constants. +// * nFriendlyFire = FALSE - Can this hurt allies? Best thing is to put +// GlobalFriendlyFireHostile - GetIsReactionTypeHostile(oTarget) == TRUE +// GlobalFriendlyFireFriendly - GetIsReactionTypeFriendly(oTarget) == FALSE +// FALSE - Cannot hurt allies (GetIsEnemy/!GetIsFriend used) +// * iDeathImmune = FALSE - Does it use a death save? (!GetIsImmune) +// * iNecromanticSpell = FALSE - Is it a necromancy spell? Undead are also checked in this. +object AI_GetBestAreaSpellTarget(float fRange, float fSpread, int nLevel, int iSaveType = FALSE, int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE, int iDeathImmune = FALSE, int iNecromanticSpell = FALSE); + +// Returns the object to the specifications: +// Within nRange (float) +// The most targets around the creature in nRange, in nSpread. +// Can be the caster, of course +//object AI_GetBestFriendyAreaSpellTarget(float fRange, float fSpread, int nShape = SHAPE_SPHERE); + +// Returns 1-4 for tiny-huge weapons. Used for disarm etc. +int AI_GetWeaponSize(object oItem); +// This returns TRUE if the target will always resist the spell given the parameters. +// - Uses GlobalOurChosenClassLevel for our level check. +int AI_SpellResistanceImmune(object oTarget); +// If the target will always save against iSaveType, and will take no damage, returns TRUE +// * Target is GlobalSpellTarget. Save is GlobalSpellTargetWill ETC. +// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE +// * iSpellLevel - Level of the spell being cast. +int AI_SaveImmuneSpellTarget(int iSaveType, int iSpellLevel); +// If the target will always save against iSaveType, and will take no damage, returns TRUE +// * oTarget - who saving against spell. +// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE +// * The save used is GetReflexSavingThrow* ETC. +// * iSpellLevel - Level of the spell being cast. +int AI_SaveImmuneAOE(object oTarget, int iSaveType, int iSpellLevel); +// Gets the percent, of X vs Y, iNumber/iTotal * 100 = %. +int AI_GetPercentOf(int iNumber, int iTotal); +// This returns a number, 1-4. This number is the levels +// of spell they will be totally immune to. +int AI_GetSpellLevelEffect(object oTarget); +// Imput oTarget and iLevel and it will check if they are automatically +// immune to the spell being cast. +int AI_GetSpellLevelEffectAOE(object oTarget, int iLevel = 9); +// Returns TRUE if any of the checks the oGroupTarget is immune to. +int AI_AOEDeathNecroCheck(object oGroupTarget, int iNecromanticSpell, int iDeathImmune); +// This will do 1 of two things, with the spell ID +// 1. If iHealAmount is FALSE, it will return what number (rank) in order, which is also used for level checking +// 2. If TRUE, it will return the average damage it will heal. +// iSelf - Used for the odd spells "cure other"'s +int AI_ReturnHealingInfo(int iSpellID, int iSelf = FALSE, int iHealAmount = FALSE); +// TRUE if the spell is one recorded as being cast before in time stop. +// - Checks Global "Are we in time stop?" and returns FALSE if not in time stop +int AI_CompareTimeStopStored(int nSpell, int nSpell2 = 0, int nSpell3 = 0, int nSpell4 = 0); +// Sets the spell to be stored in our time stop array. +void AI_SetTimeStopStored(int nSpell); +// Deletes all time stopped stored numbers. +void AI_DeleteTimeStopStored(); +// Sets the Global Hex for the enemy spell target immunties. +// 7+ Intelligence also uses GetIsImmune. +// The target is GlobalSpellTarget. +// - Uses effects loop and GetIsImmune to comprehend the most. +void AI_SortSpellImmunities(); +// This will, in most occasion, ClearAllActions. +// If it does NOT, it returns FALSE, if we are doing something more important, +// and we perform that action again (or carry on doing it). +// - This also sets shadowdancer hiding if no one has trueseeing nearby +int AI_StopWhatWeAreDoing(); +// Turn of hiding if turn of timer is on, and turn of searching if it is +// active. +// This should be called before any action using a feat, spell or similar, if +// we need to move. +void AI_ActionTurnOffHiding(); + +// Simple return TRUE if it matches hex GlobalTargetImmunitiesHex +int AI_GetSpellTargetImmunity(int iImmunityHex); +// Sets iImmunityHex to GlobalTargetImmunitiesHex. +void AI_SetSpellTargetImmunity(int iImmunityHex); + +// This returns an object, not seen not heard ally, who we +// might flee to. Uses a loop, and runs only when we are going to flee for sure. +object AI_GetNearbyFleeObject(); +// This returns the best primary or secondary weapon from sArray. +// It actually returns the number value, IE the place it is at. Easy to get all info from there. +// - Use iType... +// - 0 = Highest Value :-) +// - 1 = Highest Damage +// - 2 = Biggest +// - 3 = Smallest +int AI_GetWeaponArray(string sArray, object oCannotBe = OBJECT_INVALID, int iType = 0); +// Just sorts out sOriginalArrayName to sNewArrayName based on range only. +// - Also sets maxint to MAXINT_ + sNewArrayName +// - Closest to futhest +void AI_TargetingArrayDistanceStore(string sOriginalArrayName, string sNewArrayName); +// Just sorts out sOriginalArrayName to sNewArrayName based on iType. +// iType 1 = AC, iType 2 = Total Saves, iType 3 = Phisical Protections, +// iType 4 = BAB, iType 5 = Hit Dice, iType 6 = Percent HP, iType 7 = Current HP, +// iType 8 = Maximum HP. 9 = Attacking us or not. 10 = Is a PC. +void AI_TargetingArrayIntegerStore(int iType, string sOriginalArrayName); +// This sets ARRAY_TEMP_ARRAY of integer values to sNewArrayName. +// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER. +// - We work until iMinimum is filled, or we get to iMinimum and we get to +// a target with value > iImputMinimum. (20 - 25 > X?) +// Returns the amount of targets set in sNewArrayName. +int AI_TargetingArrayLimitTargets(string sNewArrayName, int iTypeOfTarget, int iImputMinLimit, int iMinLoop, int iMaxLoop); +// This sets ARRAY_TEMP_ARRAY of float values to sNewArrayName. +// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER. +// - We work until iMinimum is filled, or we get to iMinimum and we get to +// a target with value > iImputMinimum. (20.0 - 25.0 > X?) +// Returns the amount of targets set in sNewArrayName. +int AI_TargetingArrayLimitTargetsFloat(string sNewArrayName, int iTypeOfTarget, float fImputMinLimit, int iMinLoop, int iMaxLoop); +// Deletes all FLoats, Integers and Objects set to sArray for valid +// objects got by GetLocalObject to sArray. +void AI_TargetingArrayDelete(string sArray); +// Makes sure oTarget isn't: +// - Dead +// - Petrified +// - AI Ignore ON +// - DM +// Must be: Seen or heard +// Returns: TRUE if any of these are true. +int AI_GetTargetSanityCheck(object oTarget); + +/*:://///////////////////////////////////////////// +//:: Name: AI_SetSpellTargetImmunity, AI_GetSpellTargetImmunity +//:://///////////////////////////////////////////// + Immunity settings for spells. +//:://///////////////////////////////////////////*/ + +// Simple return TRUE if it matches hex. +// - Uses GlobalSpellTarget for target object +int AI_GetSpellTargetImmunity(int iImmunityHex) +{ + return (GlobalTargetImmunitiesHex & iImmunityHex); +} +// Sets iImmunityHex to GlobalTargetImmunitiesHex. +void AI_SetSpellTargetImmunity(int iImmunityHex) +{ + GlobalTargetImmunitiesHex = GlobalTargetImmunitiesHex | iImmunityHex; +} +/*:://///////////////////////////////////////////// +//:: Name: AI_SetUpUsEffects +//:://///////////////////////////////////////////// + Sets up the effects. Used before the uncommandable, so we + know if we are or not! (EG stun) +//:://///////////////////////////////////////////*/ +void AI_SetUpUsEffects() +{ + // SetLocal to stop other AI casters ETC from setting effect + SetLocalInt(OBJECT_SELF, AI_JASPERRES_EFFECT_SET, TRUE); + // Set locals and whatever on self. + AI_SetEffectsOnTarget(); + // Set global time stop + GlobalInTimeStop = AI_GetAIHaveEffect(GlobalEffectTimestop); + if(!GlobalInTimeStop) + { + GlobalInTimeStop = GetLocalInt(OBJECT_SELF, "SPELL_TS_CAST"); + } +} +// Gets the percent, of X vs Y, iNumber/iTotal * 100 = %. +int AI_GetPercentOf(int iNumber, int iTotal) +{ + return FloatToInt((IntToFloat(iNumber)/IntToFloat(iTotal)) * i100); +} +// Special: Apply EffectCutsceneImmobilize +void AI_SpecialActionApplyItem() +{ + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneImmobilize(), OBJECT_SELF); +} +// Special: Remove EffectCutsceneImmobilize +void AI_SpecialActionRemoveItem() +{ + effect eCheck = GetFirstEffect(OBJECT_SELF); + while(GetIsEffectValid(eCheck)) + { + if(GetEffectType(eCheck) == EFFECT_TYPE_CUTSCENEIMMOBILIZE && + GetEffectSpellId(eCheck) == iM1) RemoveEffect(OBJECT_SELF, eCheck); + eCheck = GetNextEffect(OBJECT_SELF); + } +} +// Gets a item talent value, applies EffectCutsceneImmobilize then removes it. +// - iTalent, 1-21. +void AI_SetItemTalentValue(int iTalent) +{ + // Apply EffectCutsceneImmobilize + AI_SpecialActionApplyItem(); + + // Simply get the best. + talent tCheck = GetCreatureTalentBest(iTalent, i20); + int iValue = GetIdFromTalent(tCheck); + + // Set to constant + SetAIConstant(ITEM_TALENT_VALUE + IntToString(iTalent), iValue); + + // Remove EffectCutsceneImmobilize + AI_SpecialActionRemoveItem(); +} +// This checks if nTalent is one that will be checked for by items. +int AI_GetSpellCategoryHasItem(int nTalent) +{ + // 1, 2, 3, - , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + if(nTalent == i4 || nTalent == i5 || nTalent >= i16 || nTalent < i1) + { + return FALSE; + } + return TRUE; +} +/*:://///////////////////////////////////////////// +//:: Name: AI_SetTimeStopStored +//:://///////////////////////////////////////////// + Sets the spell to be stored in our time stop array. +//:://///////////////////////////////////////////*/ +void AI_SetTimeStopStored(int nSpell) +{ + // Time stop check + if(!GlobalInTimeStop) return; + if(GlobalTimeStopArraySize < i0) + { + GlobalTimeStopArraySize = i1;// Size is now 1 + SetAIConstant(TIME_STOP_LAST_ + IntToString(i1), nSpell); + SetAIInteger(TIME_STOP_LAST_ARRAY_SIZE, GlobalTimeStopArraySize); + } + else if(GlobalTimeStopArraySize == i0) + { + GlobalTimeStopArraySize = GetAIInteger(TIME_STOP_LAST_ARRAY_SIZE); + GlobalTimeStopArraySize++; + SetAIConstant(TIME_STOP_LAST_ + IntToString(GlobalTimeStopArraySize), nSpell); + SetAIInteger(TIME_STOP_LAST_ARRAY_SIZE, GlobalTimeStopArraySize); + } + else // Is over 0 + { + GlobalTimeStopArraySize++; + SetAIConstant(TIME_STOP_LAST_ + IntToString(GlobalTimeStopArraySize), nSpell); + SetAIInteger(TIME_STOP_LAST_ARRAY_SIZE, GlobalTimeStopArraySize); + } +} +/*:://///////////////////////////////////////////// +//:: Name: DeleteTimeStopStored +//:://///////////////////////////////////////////// + Deletes all time stopped stored numbers. +//:://///////////////////////////////////////////*/ +void AI_DeleteTimeStopStored() +{ + GlobalTimeStopArraySize = GetAIInteger(TIME_STOP_LAST_ARRAY_SIZE); + if(GlobalTimeStopArraySize) + { + int iCnt; + for(iCnt = i1; iCnt <= GlobalTimeStopArraySize; iCnt++) + { + DeleteAIConstant(TIME_STOP_LAST_ + IntToString(iCnt)); + } + } + DeleteAIInteger(TIME_STOP_LAST_ARRAY_SIZE); + GlobalTimeStopArraySize = iM1; +} +/*:://///////////////////////////////////////////// +//:: Name: GetWeaponSize +//:://///////////////////////////////////////////// + Returns 1-4 for tiny-huge weapons. Used for disarm etc. +//::////////////////////////////////////////////*/ +int AI_GetWeaponSize(object oItem) +{ + // Ignore invalid weapons + if(!GetIsObjectValid(oItem)) return FALSE; + + // Returns shields as 0 + switch(GetBaseItemType(oItem)) + { + // Tiny + // 22, 42, 59. + case BASE_ITEM_DAGGER: + case BASE_ITEM_KUKRI: + case BASE_ITEM_SHURIKEN: + return i1;// WEAPON_SIZE_TINY + break; + // Small + // 0, 7, 9, 14, 31, 37, 38, 40, 60, 61, 63 + case BASE_ITEM_SHORTSWORD: + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_LIGHTMACE: +// case BASE_ITEM_SMALLSHIELD: + case BASE_ITEM_DART: + case BASE_ITEM_LIGHTHAMMER: + case BASE_ITEM_HANDAXE: + case BASE_ITEM_KAMA: + case BASE_ITEM_SICKLE: + case BASE_ITEM_SLING: + case BASE_ITEM_THROWINGAXE: + case BASE_ITEM_WHIP: // Hordes + return i2;// WEAPON_SIZE_SMALL + break; + // Medium + // 1, 2, 3, 4, 5, 6, 11, 28, 41, 47, 51, 53, 56 + // 1-6 = + // BASE_ITEM_LONGSWORD, BASE_ITEM_BATTLEAXE, BASE_ITEM_BASTARDSWORD + // BASE_ITEM_LIGHTFLAIL, BASE_ITEM_WARHAMMER, BASE_ITEM_HEAVYCROSSBOW + case BASE_ITEM_LONGSWORD: + case BASE_ITEM_BATTLEAXE: + case BASE_ITEM_BASTARDSWORD: + case BASE_ITEM_LIGHTFLAIL: + case BASE_ITEM_WARHAMMER: + case BASE_ITEM_HEAVYCROSSBOW: + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_CLUB: + case BASE_ITEM_KATANA: + case BASE_ITEM_MORNINGSTAR: + case BASE_ITEM_RAPIER: + case BASE_ITEM_SCIMITAR: +// case BASE_ITEM_LARGESHIELD: + case BASE_ITEM_DWARVENWARAXE: // Hordes + return i3;// WEAPON_SIZE_MEDIUM; + break; + // Large weapons + // 8, 10, 12, 13, 18, 32, 33, 35, 45, 50, 55, 57, 58 + case BASE_ITEM_LONGBOW: + case BASE_ITEM_HALBERD: + case BASE_ITEM_TWOBLADEDSWORD: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_DIREMACE: + case BASE_ITEM_DOUBLEAXE: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_MAGICSTAFF: + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_SCYTHE: +// case BASE_ITEM_TOWERSHIELD: + case BASE_ITEM_SHORTSPEAR: + return i4;// WEAPON_SIZE_LARGE; + break; + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name AI_GetWeaponArray +//:://///////////////////////////////////////////// + This returns the best primary or secondary weapon from sArray. + It actually returns the number value, IE the place it is at. Easy to get all info from there. + - Use iType... + - 0 = Highest Value :-) + - 1 = Highest Damage + - 2 = Biggest + - 3 = Smallest +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ +int AI_GetWeaponArray(string sArray, object oCannotBe = OBJECT_INVALID, int iType = 0) +{ + int iMax = GetLocalInt(OBJECT_SELF, MAXINT_ + sArray); + int i, iReturn, iLastValue, iCurrentValue; + string sCurrentName; + object oCurrentWeapon; + if(iMax) + { + for(i = i1; i <= iMax; i++) // uses: "break;" to break early. + { + sCurrentName = sArray + IntToString(i); + oCurrentWeapon = GetLocalObject(OBJECT_SELF, sCurrentName);// Object + if(GetIsObjectValid(oCurrentWeapon) && + oCurrentWeapon != oCannotBe) + { + // Highest value + if(iType == i0) + { + iReturn = i;// It is highest to lowest value anyway + break; + } + // 1 = Highest Damage + else if(iType == i1) + { + iCurrentValue = GetLocalInt(OBJECT_SELF, sCurrentName + WEAP_DAMAGE); + // > because if only equal, one was higher value + if(iCurrentValue > iLastValue) + { + iLastValue = iCurrentValue; + iReturn = i; + } + } + // 2 = Biggest + // 3 = Smallest + else + { + iCurrentValue = GetLocalInt(OBJECT_SELF, sCurrentName + WEAP_SIZE); + if(iType == i2) + { + if(iCurrentValue > iLastValue)// > because if only equal, one was higher value + { + iLastValue = iCurrentValue; + iReturn = i; + } + } + else // if(iType == i3) + { + if(iCurrentValue < iLastValue)// > because if only equal, one was higher value + { + iLastValue = iCurrentValue; + iReturn = i; + } + } + } + } + } + } + return iReturn; +} +// Equips the best shield we have. +// - Used before we cast a spell so casters can gain maximum AC. +void AI_EquipBestShield() +{ + object oShield = GetAIObject(AI_WEAPON_SHIELD); + if(GetIsObjectValid(oShield) && + GetItemPossessor(oShield) == OBJECT_SELF && + GlobalLeftHandWeapon != oShield) + { + ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); + } +} + +// Turns on/off all melee things (not stealth or search!) but not iMode. +void AI_SetMeleeMode(int iMode = -1) +{ + if(iMode != iM1) + { + if(!GetActionMode(OBJECT_SELF, iMode)) + { + SetActionMode(OBJECT_SELF, iMode, TRUE); + } + } + // Turn off all the rest + int iCnt; + // 0 = stealth, 1 = stealth (ignore these 2), 3 = parry. 11 = DF. + for(iCnt = ACTION_MODE_PARRY; iCnt <= ACTION_MODE_DIRTY_FIGHTING; iCnt++) + { + if(iCnt != iMode) + { + if(GetActionMode(OBJECT_SELF, iCnt)) + { + SetActionMode(OBJECT_SELF, iCnt, FALSE); + } + } + } +} + +// Turn of hiding if turn of timer is on, and turn of searching if it is +// active. +// This should be called before any action using a feat, spell or similar, if +// we need to move. +void AI_ActionTurnOffHiding() +{ + // Turn of searching and hiding here, if we want to! + if(!GetHasFeat(FEAT_KEEN_SENSE) && GetDetectMode(OBJECT_SELF) == DETECT_MODE_ACTIVE) + { + SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, FALSE); + } + // Turn of hiding if we have been seen lately. + if(GetLocalTimer(AI_TIMER_TURN_OFF_HIDE) && + GetStealthMode(OBJECT_SELF) == STEALTH_MODE_ACTIVATED) + { + SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE); + } +} + +/*:://///////////////////////////////////////////// +//:: Name AI_EquipAndAttack +//:://///////////////////////////////////////////// + This was a bioware script. It has been changed a lot. + Best target if normal int (equal or more than 2). + + Will play a random attack taunt, sometimes. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ +int AI_EquipAndAttack() +{ + // Taunt the enemy! + if(!GetSpawnInCondition(AI_FLAG_OTHER_NO_PLAYING_VOICE_CHAT, AI_OTHER_MASTER) + && d100() < i7) + { + int iVoice = VOICE_CHAT_ATTACK;// Default + switch(Random(i6)) // Random will do 0-5, so more chance of ATTACK + { + case i0: iVoice = VOICE_CHAT_LAUGH; break; + case i1: iVoice = VOICE_CHAT_BATTLECRY1; break; + case i2: iVoice = VOICE_CHAT_BATTLECRY2; break; + case i3: iVoice = VOICE_CHAT_BATTLECRY3; break; + default: iVoice = VOICE_CHAT_ATTACK; break;// Default + } + // Random delay for 0.0 to 1.0 seconds. + float fDelay = IntToFloat(Random(10) + 1) / 10.0; + DelayCommand(fDelay, PlayVoiceChat(iVoice)); + } + + // - Flying + if(GlobalRangeToMeleeTarget > f8 && + GetSpawnInCondition(AI_FLAG_COMBAT_FLYING, AI_COMBAT_MASTER) && + !GetHasSpellEffect(SPELL_BIGBYS_CRUSHING_HAND) && !GetHasSpellEffect(SPELL_BIGBYS_GRASPING_HAND)) + { + SetAIObject(AI_FLYING_TARGET, GlobalMeleeTarget); + ExecuteScript(FILE_FLY_ATTACK, OBJECT_SELF); + return TRUE; + } + + // Set up the range to use weapons at, before moving into HTH + // Default is 5.0 (+ Some for creature size) with no changes... + float fRange = f5; + // Might have some pre-set range OnSpawn + int iRangeFromSetup = GetAIInteger(AI_RANGED_WEAPON_RANGE); + if(iRangeFromSetup >= i1) + { + fRange = IntToFloat(iRangeFromSetup); + } + // If our intelligence is enough, we make it 3.0. + else if(GlobalIntelligence >= i5) + { + fRange = f3; + } + // We add a base of X for monster sizes + fRange += (IntToFloat(GlobalOurSize)/f4); + + // iRangedAttack = TRUE, then equip ranged weapons + int iRangedAttack = FALSE; + + // Now check for it... + // If range to melee target is OVER fRange... + // - Or setting to always use bow + if(GlobalRangeToMeleeTarget > fRange || + GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER)) + { + iRangedAttack = TRUE; + } + + // Special check for AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND. + // Either a % chance, or that they have no enemy in X distance, and we are + // in Y distance. + // We always run in at 8 or less M too. + if(iRangedAttack == TRUE && GetSpawnInCondition(AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND, AI_COMBAT_MASTER)) + { + // If they are under 8M away, always run in - 80% chance + if(GlobalRangeToMeleeTarget < f8 && d10() <= i8) + { + iRangedAttack = FALSE; + } + else + { + // Get distance from melee target to nearest ally to it. + float fAllyToMelee = GetDistanceBetween(GlobalMeleeTarget, GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, GlobalMeleeTarget, i1, CREATURE_TYPE_IS_ALIVE, TRUE)); + + // Check thier range to ours + // - Basically, if GlobalRangeToMeleeTarget - fAllyToMelee, is + // a difference of Random(4) + 4;, we move in. + // + 60% chance! + if((GlobalRangeToMeleeTarget - fAllyToMelee) <= (IntToFloat(Random(i4) + i4)) && + d10() <= i6) + { + iRangedAttack = FALSE; + } + } + } + + // Declare everything + object oEquipped, oMainWeapon, oMainPossessor, oPrimary, oSecondary, oTwohanded; + int iPickUpDisarmed, iRanged, iShield, iValidAmmo, iStrength, iPrimaryNum, + iSecondaryNum, iValidPrimary, iValidSecondary, iValidTwoHanded, iPrimaryDamage, + iEquippedShield, iShieldInSlotAlready, iEquippedMostDamaging, + // Done in melee attacking, if we want more AC - IE expertise. + iNeedMoreAC; + float fRangeToWeapon; + + iShieldInSlotAlready = GetBaseItemType(GlobalLeftHandWeapon); + + // Here...we determine wether to try for melee or ranged attacks. + // This is TRUE if we did ActionEquipMostDamaging... + iEquippedMostDamaging = FALSE; + + object oShield = GetAIObject(AI_WEAPON_SHIELD); + int iValidShield = GetIsObjectValid(oShield); + // SPECIAL: We will just use default calls FIRST and stop IF we have this set + // same checks to choose between them though :-P + if(AI_GetAIHaveEffect(GlobalEffectPolymorph) || + GetSpawnInCondition(AI_FLAG_OTHER_LAG_EQUIP_MOST_DAMAGING, AI_OTHER_MASTER)) + { + // 1: "[DCR:Melee] Most Damaging Weapon. Target: " + GetName(GlobalMeleeTarget) + DebugActionSpeakByInt(1, GlobalMeleeTarget); + // iRangedAttack = 1 then ranged. + if(iRangedAttack) + { + ActionEquipMostDamagingRanged(GlobalMeleeTarget); + } + // Special near-range always attack with a bow option. + else if(GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER)) + { + ActionEquipMostDamagingRanged(GlobalMeleeTarget); + } + // Spcial - if we are set to always move back, 1/10 times we don't equip HTH. + // BUT we will attack in HTH if the last target was this target. + else if(GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER) + && d10() != i1) + { + ActionEquipMostDamagingRanged(GlobalMeleeTarget); + } + // Else we should always be in HTH range. + else // if(!iRangedAttack) + { + ActionEquipMostDamagingMelee(GlobalMeleeTarget, TRUE); + } + // Always do... + iEquippedMostDamaging = TRUE; + } + // Else normal weapons, IE we check stored ones :-P + else if(!AI_GetAIHaveEffect(GlobalEffectPolymorph)) + { + // Declaring + // Toggle: Do we pick up any disarmed weapons? + iPickUpDisarmed = GetSpawnInCondition(AI_FLAG_COMBAT_PICK_UP_DISARMED_WEAPONS, AI_COMBAT_MASTER); + // We may, if at the right range (and got a ranged weapon) equip it. + oMainWeapon = GetAIObject(AI_WEAPON_RANGED); + oMainPossessor = GetItemPossessor(oMainWeapon); + // iRanged = TRUE if we equip a ranged weapon after checks. + // iShield = TRUE if we should equip a shield + // Is the ranged weapon valid? And iRangedAttack == TRUE + // We need valid ammo, else we eqip nothing! + iValidAmmo = GetAIInteger(AI_WEAPON_RANGED_AMMOSLOT); + if(iValidAmmo == INVENTORY_SLOT_RIGHTHAND) + { + iValidAmmo = TRUE; + } + else + { + iValidAmmo = GetIsObjectValid(GetItemInSlot(iValidAmmo)); + } + // Ranged attack...valid? + if(iRangedAttack && iValidAmmo && GetIsObjectValid(oMainWeapon)) + { + // Is the possessor valid? (could be us or someone else) + if(GetIsObjectValid(oMainPossessor)) + { + // Check if it is us or not... + if(oMainPossessor == OBJECT_SELF) + { + if(GlobalRightHandWeapon != oMainWeapon) + { + // Ranged weapons then equipped in the righthand :-D + ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND); + } + oEquipped = oMainWeapon; + iRanged = TRUE;// only ranged feats. + } + // Else, we delete it and check for secondary one :-) + else if(GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED_2))) + { + // If valid, we re-set weapons! Because we cannot pick up the other. + ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF); + // As it happens immediantly (yes, more lag, gee) we check for the original :-) + oMainWeapon = GetAIObject(AI_WEAPON_RANGED); + // If it is now valid, equip it! + if(GetIsObjectValid(oMainWeapon)) + { + ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND); + oEquipped = oMainWeapon; + iRanged = TRUE;// only ranged feats. + } + } + else // Else, delete the ranged thing, someone else has it. + { + DeleteAIObject(AI_WEAPON_RANGED); + } + } + // Else, it is on the ground. If near, we pick it up. + else if(iPickUpDisarmed) + { + fRangeToWeapon = GetDistanceToObject(oMainWeapon); + if((fRangeToWeapon < f5 && fRangeToWeapon > f0)|| + !GlobalMeleeAttackers) + { + // We should attempt to pick it up... + ActionPickUpItem(oMainWeapon); + ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND); + oEquipped = oMainWeapon; + iRanged = TRUE;// only ranged feats. + } + else // Else, we can't or won't get it. + { + DeleteAIObject(AI_WEAPON_RANGED); + if(GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED_2))) + { + // If valid, we re-set weapons! Because we cannot pick up the other. + ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF); + // As it happens immediantly (yes, more lag, gee) we check for the original :-) + oMainWeapon = GetAIObject(AI_WEAPON_RANGED); + // If it is now valid, equip it! + if(GetIsObjectValid(oMainWeapon)) + { + ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND); + oEquipped = oMainWeapon; + iRanged = TRUE;// only ranged feats. + } + } + } + } + } + // This is set to the items AC value, to take away simulating taking it off when + // we check AC against other things... :-) + if(iShieldInSlotAlready == BASE_ITEM_TOWERSHIELD || + iShieldInSlotAlready == BASE_ITEM_SMALLSHIELD || + iShieldInSlotAlready == BASE_ITEM_LARGESHIELD) + { + iShieldInSlotAlready = GetItemACValue(GlobalLeftHandWeapon); + } + // NOTE: shields cannot be disarmed! If we don't have it, though, we act as if it isnt valid + if(!iValidShield || GetItemPossessor(oShield) != OBJECT_SELF) + { + // If the second is valid, we re-set weapons. + if(GetIsObjectValid(GetAIObject(AI_WEAPON_SHIELD_2))) + { + // If valid, we re-set weapons! Because we cannot pick up the other. + ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF); + } + // iValidShield is 1 if valid, and now on us. + oShield = GetAIObject(AI_WEAPON_SHIELD); + iValidShield = GetIsObjectValid(oShield); + } + // iEquippedShield is TRUE if we equip a shield. + // If something has been equipped, we arm a shield...here + if(iRanged) + { + // Need a vlaid shield AND able to equip one. + if(iValidShield && GetAIInteger(AI_WEAPON_RANGED_SHIELD)) + { + if(oShield != GetItemInSlot(INVENTORY_SLOT_LEFTHAND)) + { + ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); + } + iEquippedShield = TRUE; + } + } + else // Else, equip the best combo of HTH weapons! + { + // We run through. Of course, first, is it best for + // What do we have? + // Get things... + // First: Primary. If that is valid, we check secondary. These are arrays. + iPrimaryNum = AI_GetWeaponArray(AI_WEAPON_PRIMARY); + if(iPrimaryNum) + { + oPrimary = GetLocalObject(OBJECT_SELF, AI_WEAPON_PRIMARY + IntToString(iPrimaryNum)); + iValidPrimary = GetIsObjectValid(oPrimary); + if(iValidPrimary) + { + // We want the smallest... + iSecondaryNum = AI_GetWeaponArray(AI_WEAPON_SECONDARY, oPrimary, i3); + if(iSecondaryNum) + { + oSecondary = GetLocalObject(OBJECT_SELF, AI_WEAPON_SECONDARY + IntToString(iSecondaryNum)); + iValidSecondary = GetIsObjectValid(oSecondary); + } + } + } + // 2 handed + oTwohanded = GetAIObject(AI_WEAPON_TWO_HANDED); + iValidTwoHanded = GetIsObjectValid(oTwohanded); + // Shield is done above. + // Now, are any valid? + if(!iValidPrimary && !iValidTwoHanded) + { + ActionEquipMostDamagingMelee(GlobalMeleeTarget, TRUE); + iEquippedMostDamaging = TRUE; + } + else + { + // Now, the circumstances...what is best? Lots of damage and lower AC? + // Maybe more AC? Maybe a second weapon? (as if any are valid, its obvious better to use one). + // Globals: + // GlobalOurAC, GlobalOurHitDice, GlobalOurBaseAttackBonus, GlobalMeleeAttackers + // GlobalAverageEnemyHD, GlobalAverageEnemyBAB + iStrength = GetAbilityModifier(ABILITY_STRENGTH); + // Check one: Is a lot more AC better? Or keep shield equipped, even? + // 1. Is our AC below 2x our HD? (IE under 20 at level 10) + if((GlobalOurAC < GlobalOurHitDice * i2) || + // 2. Our AC with no shield is under average melee BAB + 6 (a badish roll) + (GlobalOurAC - iShieldInSlotAlready < GlobalAverageEnemyBAB + i6 && + GlobalAverageEnemyHD >= GlobalOurHitDice - i5) || + // 3. Melee attackers are great, over 1/4th of our HD + 2, and enemy HD is comparable toughness + (GlobalMeleeAttackers > ((GlobalOurHitDice / i4) + i2) && + GlobalAverageEnemyHD >= GlobalOurHitDice - i3)) + { + // We need more AC! + iNeedMoreAC = TRUE; + // Valid primary, for the shield... + if(iValidPrimary) + { + oEquipped = oPrimary;// May change though... + // If we have a shield (which is the point) we equip any not in slots. + if(iValidShield) + { + iEquippedShield = TRUE; + if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + if(GlobalLeftHandWeapon != oShield) + { + ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); + } + } + else if(iValidTwoHanded) + { + oEquipped = oTwohanded; + if(GlobalRightHandWeapon != oTwohanded) + { + ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND); + } + } + else if(iValidSecondary) + { + if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + if(GlobalLeftHandWeapon != oSecondary) + { + ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND); + } + } + else + { + if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + } + } + else if(iValidTwoHanded) + { + oEquipped = oTwohanded; + if(GlobalRightHandWeapon != oTwohanded) + { + ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND); + } + } + } + // Check two: If we have a good hitting chance, we might equip a 2handed first. + // 1. We have a decent strength mod, which adds damage to many 2 handed. + // This is AND things... + else if((iStrength >= (GlobalOurHitDice / i4 + i2)) && + // 2. Greater strength then dexterity + (iStrength > GetAbilityModifier(ABILITY_DEXTERITY) + Random(i3))) + { + if(iValidTwoHanded) + { + oEquipped = oTwohanded; + if(GlobalRightHandWeapon != oTwohanded) + { + ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND); + } + } + else if(iValidPrimary) + { + oEquipped = oPrimary; + // Secondary first, rather then shield. + if(iValidSecondary) + { + if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + if(GlobalLeftHandWeapon != oSecondary) + { + ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND); + } + } + else if(iValidShield) + { + iEquippedShield = TRUE; + if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + if(GlobalLeftHandWeapon != oShield) + { + ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); + } + } + else if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + } + } + // Check three: None. We equip 2 weapons, then a shield, then a 2 handed, that order. + // In essense, for 2 weapons, it is less damage more times, as it were (if we hit!) + else + { + if(iValidPrimary) + { + oEquipped = oPrimary; + // Secondary first, rather then shield. + if(iValidSecondary) + { + if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + if(GlobalLeftHandWeapon != oSecondary) + { + ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND); + } + } + else if(iValidShield) + { + iEquippedShield = TRUE; + if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + if(GlobalLeftHandWeapon != oShield) + { + ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); + } + } + else if(GlobalRightHandWeapon != oPrimary) + { + ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); + } + } + else if(iValidTwoHanded) + { + oEquipped = oTwohanded; + if(GlobalRightHandWeapon != oTwohanded) + { + ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND); + } + } + } + }// End no valid + } + }// End "lag buster" check. + + // We check for weapon effective here, if we didn't equip most damaging + // - We only do this if we are attacking the target for more then 1 round. + if(!iEquippedMostDamaging && !iRangedAttack && + GetAIInteger(AI_MELEE_TURNS_ATTACKING) >= i2) + { + // If neither weapon can damage the target...and no most damaging... + if(!GetWeaponRanged(oEquipped) && + GetIsObjectValid(oEquipped) && + !GetIsWeaponEffective(GlobalMeleeTarget)) + { + // 2: "[DCR:Melee] Most Damaging as Not Effective" + DebugActionSpeakByInt(2); + // We equip a shield (if not already) + if(!iEquippedShield && iValidShield) + { + if(oShield != GetItemInSlot(INVENTORY_SLOT_LEFTHAND)) + { + ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); + } + } + // And equip most damaging (melee) + ActionEquipMostDamagingMelee(GlobalMeleeTarget); + } + } + // Now, we should have equipped something :-D + // GlobalLeftHandWeapon, GlobalRightHandWeapon + // GlobalOurSize, GlobalOurBaseAttackBonus, GlobalMeleeTargetAC, GlobalMeleeTargetBAB + + // We randomly hit, determined by our intelligence. + // If we have higher intelligence, we take our rolls to be higher, so we use feats more. + int iOurHit; + // add a base value...so 18 for Int. 10. 0 for Int. 1. + iOurHit = GlobalOurBaseAttackBonus + ((GlobalIntelligence * i2) - i2); + // Randomise...should never get results over 20 now. (0-20 for INT 1, 0-2 for INT 10) + iOurHit += Random(i20 - ((GlobalIntelligence - i1) * i2)); + + // 1.3 - Add Dexterity to ranged feat checking, strength to melee + + // Note: + // - 8+ Intelligence also checks DISCIPLINE. + +/* Ability: Strength. + Requires Training: No. + Classes: All. + + A successful check allows the character to resist any combat feat + (Disarm, Called Shot, or Knockdown).n). + + Check: The DC is equal to the attacker's attack roll. + Use: Automatic. */ + + // We therefore make ValidFeat mean FALSE if the target has too much + // disipline (by a margin) + if(GlobalIntelligence >= i8) + { + // If thier rank - 20 is over our BAB + XX, then no go, as it is very + // likely they'll pass the check. + if(GetSkillRank(SKILL_DISCIPLINE) - i20 >= iOurHit) + { + // No feats + ValidFeats = FALSE; + } + } + + // Note: If we can only hit on a 20, and have 7+ Intelligence, we make it + // use ANY feat (as they will have the same chance of hitting - a critical - + // as a normal attack) + if(GlobalOurBaseAttackBonus + i20 <= GlobalMeleeTargetAC) + { + iOurHit = GlobalOurBaseAttackBonus + i100;// Massive amount - we act as if we rolled 100! + } + // We turn off hiding/searching as we will at least do ActionAttack... + AI_ActionTurnOffHiding(); + + // Ranged weapon? + if(iRanged && ValidFeats && !AI_GetAIHaveEffect(GlobalEffectPolymorph)) + { + // RANGED: Add dexterity + iOurHit += GetAbilityModifier(ABILITY_DEXTERITY); + + // We see if it is a weapon which we can use power attack with >:-D + if(GetBaseItemType(oEquipped) == BASE_ITEM_THROWINGAXE) + { + if((iOurHit - i5) >= GlobalMeleeTargetAC)// Power attack + { + if((iOurHit - i10) >= GlobalMeleeTargetAC)// Improved power attack + { + AI_SetMeleeMode(ACTION_MODE_IMPROVED_POWER_ATTACK); + ActionAttack(GlobalMeleeTarget); + return FEAT_IMPROVED_POWER_ATTACK; + } + AI_SetMeleeMode(ACTION_MODE_POWER_ATTACK); + ActionAttack(GlobalMeleeTarget); + return FEAT_POWER_ATTACK; + } + } + // Rapid shot - This provides another attack, at -2 to hit. + if((iOurHit - i2) >= GlobalMeleeTargetAC && GetHasFeat(FEAT_RAPID_SHOT)) + { + AI_SetMeleeMode(ACTION_MODE_RAPID_SHOT); + ActionAttack(GlobalMeleeTarget); + return FEAT_RAPID_SHOT; + } + // Called shot is -4, but some good things...does it work though? Uncommented till sure + if(((iOurHit - i4) >= GlobalMeleeTargetAC) && + GetHasFeat(FEAT_CALLED_SHOT) && + !GetHasFeatEffect(FEAT_CALLED_SHOT, GlobalMeleeTarget)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_CALLED_SHOT, GlobalMeleeTarget); + return FEAT_CALLED_SHOT; + } + } + // Parry the enemy, if we have only 1 target attacking us, and have + // decent skill. + else if(!iRanged && !GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PARRYING, AI_OTHER_COMBAT_MASTER) && + // 1 attacker, and the melee target is attacking us. + GlobalMeleeAttackers <= i1 && GetAttackTarget(GlobalMeleeTarget) == OBJECT_SELF && + // Not got a ranged weapon - enemy target that is + !GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, GlobalMeleeTarget)) && + // Got skill rank of at least our hit dice + 4. + ((GetSkillRank(SKILL_PARRY) >= (GlobalOurHitDice + i4)) || + // Or forced + GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PARRYING, AI_OTHER_COMBAT_MASTER))) + { + // Set parry mode + AI_SetMeleeMode(ACTION_MODE_PARRY); + ActionAttack(GlobalMeleeTarget); + return AI_PARRY_ATTACK; + } + else if(!iRanged) + { + // Death attack - if we are hidden, or we have invisiblity effects + // - A basic "seen or heard" check, backwards (slightly cheating!) + if(GetHasFeat(FEAT_PRESTIGE_DEATH_ATTACK_1) && + (!GetObjectSeen(OBJECT_SELF, GlobalMeleeTarget) || + !GetObjectHeard(OBJECT_SELF, GlobalMeleeTarget))) + { + // This doesn't seem to be "useable" and is meant to be automatic. + // However, using something that decreases our attack will be silly + // so we will just ActionAttack the target. + ActionAttack(GlobalMeleeTarget); + return FEAT_PRESTIGE_DEATH_ATTACK_1; + } + // Check for defensive stance + if(GlobalRangeToMeleeTarget < f3 && + GetLastAttackMode() != COMBAT_MODE_DEFENSIVE_STANCE) + { + // Use defensive stance on self (Hopefully these checks won't override each other) + AI_ActionUseFeatOnObject(FEAT_DWARVEN_DEFENDER_DEFENSIVE_STANCE); + // Note - fall through and carry on + } + // Added check here for valid feats, because of defensive stance. + if(!ValidFeats) + { + // If we have not used any, well...oh well! Attack!! + ActionAttack(GlobalMeleeTarget); + return AI_NORMAL_MELEE_ATTACK; + } + // MELEE + // - Add strength + iOurHit += GetAbilityModifier(ABILITY_STRENGTH); + + // More things to declare. + int iTargetWeaponSize, iTargetCreatureSize, iTargetCreatureRace, iOurWeaponSize, + iCanUseMonks, iTargetAlignment, iAddingModifier, iMonkLevels; + // Monk levels + iMonkLevels = GetLevelByClass(CLASS_TYPE_MONK); + if(!iMonkLevels) iMonkLevels = i1; + iTargetCreatureRace = GetRacialType(GlobalMeleeTarget);//done later. + iTargetAlignment = GetAlignmentGoodEvil(GlobalMeleeTarget); + // Now, monk, can it be done... + if((!GetIsObjectValid(oEquipped) || + GetBaseItemType(oEquipped) == BASE_ITEM_KAMA) && + iTargetCreatureRace != RACIAL_TYPE_CONSTRUCT && + iTargetCreatureRace != RACIAL_TYPE_UNDEAD) + { + iCanUseMonks = TRUE; + } + // Smite good, or evil! + if(iTargetAlignment != ALIGNMENT_NEUTRAL) + { + // For use against them evil pests! Top - one use only anyway. + if(iTargetAlignment == ALIGNMENT_EVIL && GetHasFeat(FEAT_SMITE_EVIL)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_SMITE_EVIL, GlobalMeleeTarget); + return FEAT_SMITE_EVIL; + } + // For use against them evil pests! Top - one use only anyway. + else if(iTargetAlignment == ALIGNMENT_GOOD && GetHasFeat(FEAT_SMITE_GOOD)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_SMITE_GOOD, GlobalMeleeTarget); + return FEAT_SMITE_GOOD; + } + } + // Ki damage :-) max roll of damage for weapons for Weapon Master - Hordes + // - Note that this a lot of uses. Test for usefulness! + if(GetHasFeat(FEAT_KI_DAMAGE)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_KI_DAMAGE, GlobalMeleeTarget); + return FEAT_KI_DAMAGE; + } + // We may use Expertiese if we are being attacked, and above we tried + // to equip a shield... + // - Need more help around us (some allies) and people atually attacking us, or low HP. + if((GlobalOurPercentHP <= i20) || + (iNeedMoreAC && GlobalMeleeAttackers && GlobalTotalAllies >= i2)) + { + // +10 AC, -10 BAB + if(GetHasFeat(FEAT_IMPROVED_EXPERTISE)) + { + AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE); + return FEAT_IMPROVED_EXPERTISE; + } + else if(GetHasFeat(FEAT_EXPERTISE)) + { + AI_SetMeleeMode(ACTION_MODE_EXPERTISE); + return FEAT_EXPERTISE; + } + } + // First, we have the little-known-about monk powerful feats... + // Instant death, what can be better? >:-D + if(iCanUseMonks && GetHasFeat(FEAT_QUIVERING_PALM) && + iOurHit + i5 >= GlobalMeleeTargetAC && + GlobalOurHitDice >= GetHitDice(GlobalMeleeTarget)) + { + // Ok, not too random. Thier roll is not d20 + fort save, it is random(15) + 5. + // - Our hit is 10 + Monk class/2 + Wisdom... + if((i10 + (iMonkLevels / i2) + GetAbilityModifier(ABILITY_WISDOM)) >= + // - This must be over thier Fortitude save, add 5 and 0-15. + (GetFortitudeSavingThrow(GlobalMeleeTarget) + i5 + Random(i15))) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_QUIVERING_PALM, GlobalMeleeTarget); + return FEAT_QUIVERING_PALM; + } + } + // We see if we want to use Whirlwind attack! + // - Check for amount of melee attackers. If 5 or more, use this. + // - We may use it later at 2 or more. :-) + // If we don't have 5 or more, we use some of the better single target + // feats before. This requires no BAB check - it is done at max BAB + if(// 90% chance of using it with 6+ melee attackers, and 8+ enemies in 4M + (d10() <= i9 && (GlobalEnemiesIn4Meters >= i8 || + GlobalMeleeAttackers >= i6)) || + // OR, 70% chance of using if we'll get more attacks if we used + // whirlwind then if we used + (d10() <= i7 && (GlobalOurBaseAttackBonus/i5 < (GlobalEnemiesIn4Meters - i1))) || + // Lastly 40% chance if we have 4+ melee, or 5+ in range + (d10() <= i4 && (GlobalEnemiesIn4Meters >= i5 || + GlobalMeleeAttackers >= i4))) + { + // - Free attack to all in 10'! This should be anyone in 6.6M or so. + if(GetHasFeat(FEAT_IMPROVED_WHIRLWIND)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_IMPROVED_WHIRLWIND, OBJECT_SELF); + // And attack after (as it doesn't seem to take a round to use) + ActionAttack(GlobalMeleeTarget); + return FEAT_IMPROVED_WHIRLWIND; + } + // All in 5' is still alright. 5 Feet = 3.3M or so. + else if(GetHasFeat(FEAT_WHIRLWIND_ATTACK)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_WHIRLWIND_ATTACK, OBJECT_SELF); + // And attack after (as it doesn't seem to take a round to use) + ActionAttack(GlobalMeleeTarget); + return FEAT_WHIRLWIND_ATTACK; + } + } + // Sap can be used by anyone, any weapon, I think... + // Almost Auto stun, great stuff. Help From toolset: +/* Sap + Type of Feat: General + Prerequisite: Base Attack Bonus +1, Called Shot. + Required for: Stunning Fist. + Specifics: A character with this feat is able to make a special stun + attack in melee. He makes an attack roll with a -4 penalty, and if the hit + successfully deals damage the defender must make a Discipline check with a + DC equal to the attacker's attack roll. If the defender fails, he or she is + dazed for 12 seconds. + Use: Selected. */ + if(!GetHasFeatEffect(FEAT_SAP, GlobalMeleeTarget) && + GetHasFeat(FEAT_SAP) && + iOurHit - i4 >= GlobalMeleeTargetAC) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_SAP, GlobalMeleeTarget); + return FEAT_SAP; + } + // Knockdown is great! One of the best ever!(and VERY, VERY overpowered) + if(!GetHasFeatEffect(FEAT_KNOCKDOWN, GlobalMeleeTarget) && + !GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN, GlobalMeleeTarget)) + { + // These return 1-5, based on size. + iTargetCreatureSize = GetCreatureSize(GlobalMeleeTarget); + // By far the BEST feat to use - knocking them down lets you freely attack them! + if(GetHasFeat(FEAT_IMPROVED_KNOCKDOWN)) + { + // Imporved affects like if we were 1 size larger - thus we can affect 2 sizes bigger targets + if((GlobalOurSize + i2) >= iTargetCreatureSize) + { + // Modifier, adds anything from -4 to 0 to 4. + // Test: Us, size 3, them size 1. 3 - 1 = 2. 2 * 4 = +8 to hit. + iAddingModifier = (GlobalOurSize - iTargetCreatureSize) * i4; + // We are 1 size bigger, so its evens (we add 4 onto -4) + if(iAddingModifier + iOurHit >= GlobalMeleeTargetAC) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_IMPROVED_KNOCKDOWN, GlobalMeleeTarget); + return FEAT_IMPROVED_KNOCKDOWN; + } + } + } + // Knockdown, we can hit on 1 size above or smaller. + else if(GetHasFeat(FEAT_KNOCKDOWN)) + { + // Only works on our size, above 1, or smaller. + if((GlobalOurSize + i1) >= iTargetCreatureSize) + { + // Same as above, but we always take 4 more. + iAddingModifier = ((GlobalOurSize - iTargetCreatureSize) * i4) - (i4); + // Calculate + if(iAddingModifier + iOurHit >= GlobalMeleeTargetAC) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_KNOCKDOWN, GlobalMeleeTarget); + return FEAT_KNOCKDOWN; + } + } + } + } + // Define sizes of weapons, ETC. + // Check if they are disarmable. + if(GetIsCreatureDisarmable(GlobalMeleeTarget)) + { + iOurWeaponSize = AI_GetWeaponSize(oEquipped); + iTargetWeaponSize = AI_GetWeaponSize(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, GlobalMeleeTarget)); + // No AOO, and only a -4 penalty to hit. + if(GetHasFeat(FEAT_IMPROVED_DISARM)) + { + // We need to have valid sizes, so no odd weapons or shields to attack with... + if(iOurWeaponSize && iTargetWeaponSize)// Are both != 0? + { + // Apply weapon size penalites/bonuses to check - Use right weapons. + // We times it by 4. + // Test: Us, size 3, them size 1. (3 - 1 = 2) then (2 * 4 = 8) So +8 to hit. + iAddingModifier = (iOurWeaponSize - iTargetWeaponSize) * i4; + if((iAddingModifier + iOurHit - i4) >= GlobalMeleeTargetAC) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_IMPROVED_DISARM, GlobalMeleeTarget); + return FEAT_IMPROVED_DISARM; + } + } + } + // Provokes an AOO. Improved does not, but this is -6, + // and bonuses depend on weapons used (sizes) + else if(GetHasFeat(FEAT_DISARM)) + { + // We need to have valid sizes, so no odd weapons or shields to attack with... + if(iOurWeaponSize && iTargetWeaponSize)// Are both != 0? + { + // Apply weapon size penalites/bonuses to check - Use left weapons. + iAddingModifier = (iOurWeaponSize - iTargetWeaponSize) * i4; + // We take 6 and then 1 per melee attacker (AOOs) + if((iAddingModifier + iOurHit - i6 - GlobalMeleeAttackers) >= GlobalMeleeTargetAC) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_DISARM, GlobalMeleeTarget); + return FEAT_DISARM; + } + } + } + } + // Next, stunning fist. + // Stuns the target, making them unable to move. -4 attack. + // DC (fort) of 10 + HD/2 + wis mod. + if(iCanUseMonks && + !GetHasFeatEffect(FEAT_STUNNING_FIST, GlobalMeleeTarget) && + GetHasFeat(FEAT_STUNNING_FIST)) + { + // Start adding modifier at 0. + iAddingModifier = i0; + // If not a monk, its -4 to hit. Monk levels defaults to 1 if 0 + if(iMonkLevels == TRUE) iAddingModifier - i4; + // We hit ETC. + // Save is above + if(iOurHit >= GlobalMeleeTargetAC && + // Save + (i10 + (GlobalOurHitDice / i2) + GetAbilityModifier(ABILITY_WISDOM) + >= GetFortitudeSavingThrow(GlobalMeleeTarget) + i5 + Random(i15))) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_STUNNING_FIST, GlobalMeleeTarget); + return FEAT_STUNNING_FIST; + } + } + // We see if we want to use Whirlwind attack! + // - Check for amount of melee attackers. If 5 or more, use this. + // - We may use it later at 2 or more. :-) + // If we don't have 5 or more, we use some of the better single target + // feats before. This requires no BAB check - it is done at max BAB + if(GlobalEnemiesIn4Meters >= i2) + { + // - Free attack to all in 10'! This should be anyone in 6.6M or so. + if(GetHasFeat(FEAT_IMPROVED_WHIRLWIND)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_IMPROVED_WHIRLWIND, OBJECT_SELF); + return FEAT_IMPROVED_WHIRLWIND; + } + // All in 5' is still alright. 5 Feet = 3.3M or so. + else if(GetHasFeat(FEAT_WHIRLWIND_ATTACK)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_WHIRLWIND_ATTACK, OBJECT_SELF); + return FEAT_WHIRLWIND_ATTACK; + } + } + // Next, flurry of blows... + if(iCanUseMonks && + iOurHit - i2 >= GlobalMeleeTargetAC && + GetHasFeat(FEAT_FLURRY_OF_BLOWS)) + { + AI_SetMeleeMode(ACTION_MODE_FLURRY_OF_BLOWS); + ActionAttack(GlobalMeleeTarget); + return FEAT_FLURRY_OF_BLOWS; + } + // Expertise, special case: If we have a high BAB verus thier AC. + // Only basic for now... + if(GetHasFeat(FEAT_IMPROVED_EXPERTISE)) + { + // Our hit is over thier AC, and thier BAB hits us all the time... + // OR when there are 3+ + if((iOurHit >= GlobalMeleeTargetAC && + GlobalMeleeTargetBAB + i5 >= GlobalOurAC) || // Add 5 to thier hit + GlobalMeleeAttackers >= i3) + { + AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE); + ActionAttack(GlobalMeleeTarget); + return FEAT_IMPROVED_EXPERTISE; + } + } + if(GetHasFeat(FEAT_EXPERTISE)) + { + // Expertise, we may use even if we can hit only sometimes, and + // they always hit up sometiems (50% time) + // OR when there are 1 + 1/2HD attackers. + if((iOurHit >= GlobalMeleeTargetAC && + GlobalMeleeTargetBAB + i10 >= GlobalOurAC) || // Add 10 to thier hit + GlobalMeleeAttackers >= i2) + { + AI_SetMeleeMode(ACTION_MODE_EXPERTISE); + ActionAttack(GlobalMeleeTarget); + return FEAT_EXPERTISE; + } + } + // At a -2 to hit, this can disarm the arms or legs...speed or attack bonus + if((iOurHit - i4) >= GlobalMeleeTargetAC && + GetHasFeat(FEAT_CALLED_SHOT) && + !GetHasFeatEffect(FEAT_CALLED_SHOT, GlobalMeleeTarget)) + { + AI_SetMeleeMode(); + ActionUseFeat(FEAT_CALLED_SHOT, GlobalMeleeTarget); + return FEAT_CALLED_SHOT; + } + // -10 to hit, for +10 damage. Good, I guess, in some circumstances. + // Uses base attack bonus, no additions. + if(GetHasFeat(FEAT_IMPROVED_POWER_ATTACK) && + GlobalOurBaseAttackBonus >= GlobalMeleeTargetAC) + { + AI_SetMeleeMode(ACTION_MODE_IMPROVED_POWER_ATTACK); + ActionAttack(GlobalMeleeTarget); + return FEAT_IMPROVED_POWER_ATTACK; + } + // is a -5 to hit. Uses random 5, to randomise a bit, + // I guess. Still means a massive BAB will use it. + // Uses base attack bonus, no additions. + if( GetHasFeat(FEAT_POWER_ATTACK) && + ((GlobalOurBaseAttackBonus + Random(i5)) >= GlobalMeleeTargetAC)) + { + AI_SetMeleeMode(ACTION_MODE_POWER_ATTACK); + ActionAttack(GlobalMeleeTarget); + return FEAT_POWER_ATTACK; + } + // Either: Bad chance to hit, or only one attack, we use this for 1d4 more damage + if(GetHasFeat(FEAT_DIRTY_FIGHTING) && + GlobalOurBaseAttackBonus / i5 < i1 || + GlobalOurBaseAttackBonus + i15 < GlobalMeleeTargetAC) + { + AI_SetMeleeMode(ACTION_MODE_DIRTY_FIGHTING); + ActionAttack(GlobalMeleeTarget); + return FEAT_DIRTY_FIGHTING; + } + } + + // If we have not used any, well...oh well! Attack!! + ActionAttack(GlobalMeleeTarget); + return AI_NORMAL_MELEE_ATTACK; +} +// Wrapper for AI_AttemptAttack_MeleeAttack +// Includes debug string +int AI_AttemptMeleeAttackWrapper() +{ + // Errors + // We will exit if no valid melee target/dead + if(!GetIsObjectValid(GlobalMeleeTarget) || GetIsDead(GlobalMeleeTarget)) + { + // 3: "[DCR:Melee] Melee Code. No valid melee target/Dead. Exiting" + DebugActionSpeakByInt(3); + return FALSE; + } + int iFeat = AI_EquipAndAttack(); + // 4: "[DCR:Melee] Melee attack. [Target] " + GetName(GlobalMeleeTarget) + " [Feat/Attack] " + IntToString(iFeat) + DebugActionSpeakByInt(4, GlobalMeleeTarget, iFeat); + return iFeat; +} +// Used with GlobalLastSpellValid. If GlobalLastSpellValid is 0, sets locals for +// use later, and sets GlobalLastSpellValid to the spell in question. +void AI_SetBackupCastingSpellValues(int iSpellID, int nTalent, object oTarget, int iLocation, int iRequirement, int iItemTalentValue, int iPotionTalentValue) +{ + if(GlobalLastSpellValid <= FALSE) + { + // Set last spell + GlobalLastSpellValid = iSpellID; + // Set values using 1 name, + a number + int iCnt = i1; + // SET... + // talent + SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), nTalent); + // target + iCnt++; + SetAIObject(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), oTarget); + // location + iCnt++; + SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iLocation); + // Requirements + iCnt++; + SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iRequirement); + // item + iCnt++; + SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iItemTalentValue); + // potion + iCnt++; + SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iPotionTalentValue); + } +} +/*:://///////////////////////////////////////////// +//:: Spell casting functions +//:://///////////////////////////////////////////// + Spell casting functions. +//:://///////////////////////////////////////////// +//:: Created by : Jasperre +//:://///////////////////////////////////////////*/ + +/*:://///////////////////////////////////////////// +//:: Name AttemptSpecialConcentrationCheck +//:://///////////////////////////////////////////// + This is a good check against the enemies (highest AC one) Damage against concentration + Also, based on allies, enemies, and things, may move back (random chance, bigger with + more bad things, like no stoneskins, no invisibility etc.) + We will do this more at mid-intelligence, and better checks at most intelligence. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ + +int AI_AttemptConcentrationCheck(object oTarget) +{ + // Total off check + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_CONCENTRATION, AI_OTHER_MASTER) || + // Or has no moving back needed. + GetHasFeat(FEAT_EPIC_IMPROVED_COMBAT_CASTING) || + // Or has all spells quickened! + GetHasFeat(FEAT_EPIC_AUTOMATIC_QUICKEN_3) || + // - Ignore 1, but not 3 or 2. + GetHasFeat(FEAT_EPIC_AUTOMATIC_QUICKEN_2)) + { + return FALSE; + } + // Jump out if we use defensive casting! + if(GetHasSkill(SKILL_CONCENTRATION) && + // If we have 15 + 9 skill (for a level 9 spell) so we'll never fail, we + // always turn it on. + ((GetSkillRank(SKILL_CONCENTRATION) >= i24) || + // Else we'll turn it on based on our class level. Over class level + (((GetSkillRank(SKILL_CONCENTRATION) >= GlobalOurHitDice + i6) || + // Else, we'll turn it on if we have many melee attackers - the damage from + // them will be pretty high otherwise. (ONLY if they are a comparable level!) + ((GlobalMeleeAttackers >= i4 && GlobalAverageEnemyBAB + i15 >= GlobalOurAC) || + (GlobalMeleeAttackers >= i7)))))) + { + // Turn it on + // 5: "[DCR:Caster] Defensive Casting Mode ON [Enemy] " + GetName(GlobalSpellTarget) + DebugActionSpeakByInt(5, GlobalSpellTarget); + AI_SetMeleeMode(ACTION_MODE_DEFENSIVE_CAST); + return FALSE; + } + else if(GetDefensiveCastingMode(OBJECT_SELF) == DEFENSIVE_CASTING_MODE_ACTIVATED) + { + // Turn it off + SetActionMode(OBJECT_SELF, ACTION_MODE_DEFENSIVE_CAST, FALSE); + } + + if(// Check if we have the feat FEAT_EPIC_IMPROVED_COMBAT_CASTING - no AOO + !GetHasFeat(FEAT_EPIC_IMPROVED_COMBAT_CASTING) && + // We will never do anything if no melee attackers, or no ally that can help repel them + GlobalMeleeAttackers > FALSE && + // Target of the spell is not us, if it is us, we don't WANT to move!! (EG: Stoneskin casting) + oTarget != OBJECT_SELF && + // Do not move if we have protection spells, as it will be as good as we can get from stopping damage + // - May change + !AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) && + // We have an ally in 4M + GlobalValidAlly && GlobalRangeToAlly < f4 && + // Intelligence AND class mage + ((GlobalIntelligence >= i4 && + (GlobalOurChosenClass == CLASS_TYPE_WIZARD || + GlobalOurChosenClass == CLASS_TYPE_SORCERER || + GlobalOurChosenClass == CLASS_TYPE_FEY)) || + // Or override + GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_CONCENTRATION, AI_OTHER_MASTER))) + { + // First - checks concentration... + int iConcentration = GetSkillRank(SKILL_CONCENTRATION); + // If we have 0 concentration, we always run! + + + // Else we run back based on our concentration compared to how much + // damage we can take. + // - NOTE: Once we can activate defensive casting, we should do so and ignore AOO. + + // We may walk back quite often if there are quite a few allies around + // who'd benifit (IE from a person running after someone and might gain + // AOO) + // - We count up the melee enemy + // - We see how many compared to our protections + // - We move back quite often if the enemy are comparable levels and + // they are of comparable BAB (Consider average AC and average BAB) + + // We always move back if low concentration...under half our hit dice. + if(iConcentration <= GlobalOurHitDice/i2 || + // Or that the average HD is quite high (compared to 2/3 our HD) + GlobalAverageEnemyHD >= ((GlobalOurHitDice * i3) / i2) || + // Or that the average BAB is quite high (compared to 2/3 our AC) + GlobalAverageEnemyBAB >= ((GlobalOurAC * i3) / i2)) + { + // We check the counter + int iCounter = GetAIInteger(AI_CONCENTRATIONMOVE_COUNTER); + iCounter++; + SetAIInteger(AI_CONCENTRATIONMOVE_COUNTER, iCounter); + // If the counter is <= 5, we will move back, else we've been moving + // back for 5 turns already! Stop and do something useful... + if(iCounter <= i5) + { + ClearAllActions(); + // 6: "[DCR:Caster] Moving away from AOO's. [Enemy] " + GetName(GlobalSpellTarget) + DebugActionSpeakByInt(6, GlobalSpellTarget); + ActionMoveAwayFromLocation(GetLocation(GlobalMeleeTarget), TRUE, f10); + return TRUE; + } + else if(iCounter >= i10) + { + // Reset once we get to 10 rounds. + DeleteAIInteger(AI_CONCENTRATIONMOVE_COUNTER); + } + else + { + // Counter between 5 and 10 - do normal things + return FALSE; + } + } + // If we don't move back, we reset the counter for time we have moved back + DeleteAIInteger(AI_CONCENTRATIONMOVE_COUNTER); + } + return FALSE; +} +// Special case - it checks the talent again, in EffectCutsceneImmobilize (if not already so) and +// uses the item, if it is an equal talent. +int AI_ActionCastItemEqualTo(object oTarget, int iSpellID, int iLocation) +{ + // We need to get what one is actually the talent number :-D + // This is actually faster, as we know that iSpellID equals one of them :-D + // Set to local integers + int iCnt, nTalent, iReturn; + talent tBestOfIt; + for(iCnt = i1; iCnt <= i21; iCnt++) + { + // Check match... + if(GetAIConstant(ITEM_TALENT_VALUE + IntToString(iCnt)) == iSpellID) + { + // We break with set one. + nTalent = iCnt; + break; + } + } + // Check for valid talent (1+) + if(nTalent >= i1) + { + // Apply EffectCutsceneImmobilize. It only removes ones with no spell ID anyway :-D + AI_SpecialActionApplyItem(); + + tBestOfIt = GetCreatureTalentBest(nTalent, i20); + // JUST to make sure! + if(GetIsTalentValid(tBestOfIt) && + GetIdFromTalent(tBestOfIt) == iSpellID && + GetTypeFromTalent(tBestOfIt) == TALENT_TYPE_SPELL) + { + AI_SetTimeStopStored(iSpellID); + // 7: "[DCR:Casting] Talent(item) [TalentID] " + IntToString(GetIdFromTalent(tBestOfIt)) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation) + DebugActionSpeakByInt(7, oTarget, GetIdFromTalent(tBestOfIt), IntToString(iLocation)); + // Sets a sub spell whatever, just in case. + SetLocalInt(OBJECT_SELF, AI_SPELL_SUB_SPELL_CAST, iSpellID); + // Equip the best shield we have + AI_EquipBestShield(); + // Use this only for items, so we should not have the spell. + if(iLocation) + { + ActionUseTalentAtLocation(tBestOfIt, GetLocation(oTarget)); + } + else //if(!GetObjectSeen(oTarget)) // Should be seen - checked before. + { + ActionUseTalentOnObject(tBestOfIt, oTarget); + } + iReturn = TRUE; + } + // remove the EffectCutsceneImmobilize, if so. + AI_SpecialActionRemoveItem(); + } + // Return TRUE or FALSE. + return iReturn; +} +// This is used for INFLICT spells, as GetHasSpell also can return 1+ for +// any extra castings - like if we had 2 light wounds and 2 blesses, it'd return +// 4. +// Imput the iSpellID, oTarget in to cast the spell. TRUE if casted. No items checked. +int AI_ActionCastSpontaeousSpell(int iSpellID, int nTalent, object oTarget) +{ + if(nTalent > i0 && GetHasSpell(iSpellID) && GetObjectSeen(oTarget)) + { + //Added by Shayan on 21/02/2005 + //To stop from clerics using inflict wounds spells, instead of their other spells. + if(iSpellID == SPELL_INFLICT_MINOR_WOUNDS || iSpellID == SPELL_INFLICT_LIGHT_WOUNDS + || iSpellID == SPELL_INFLICT_MODERATE_WOUNDS || iSpellID == SPELL_INFLICT_SERIOUS_WOUNDS + || iSpellID == SPELL_INFLICT_CRITICAL_WOUNDS) + { + int iTimesCast = GetLocalInt(OBJECT_SELF, "INFLICT_CAST"); + if(iTimesCast >= 3) + { return FALSE; } + iTimesCast++; + SetLocalInt(OBJECT_SELF, "INFLICT_CAST", iTimesCast); + } + // Note: Not stored or used in time stop + // 8: "[DCR: Casting] Workaround for Spontaeous [SpellID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + DebugActionSpeakByInt(8, oTarget, iSpellID); + // Equip the best shield we have + AI_EquipBestShield(); + // Decrement the spell being cast by one as we cheat cast it + DecrementRemainingSpellUses(OBJECT_SELF, iSpellID); + // Cheat cast, it'll remove inflict wounds if it is an inflict spell anyway. + ActionCastSpellAtObject(iSpellID, oTarget, METAMAGIC_NONE, TRUE); + return TRUE; + } + return FALSE; +} +// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical): +// 1. If they have the spell normally +// 2. If they have an item with the spell. +// 3. If they have a potion of the right type. +// - We always attack with a bow at ranged, but may attack normally after the spell. +// - If nTalent is 0, we do not check items. +// - If iRequirement is 0, it is considered innate. +// - Imput iItemTalentValue and iPotionTalentValue to check item talents. +// - iSummonLevel can be 0, but if 1+, it is set to AI_LAST_SUMMONED_LEVEL +int AI_ActionCastSpell(int iSpellID, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1) +{ + // 1. We need nTalent to be over 0. -1 means an invalid spell for that talent, + // IE no spell for that talent + // - If iRequirement is 0, we consider it innate and no talent category for some + // reason. + if(nTalent > i0 || !iRequirement) + { + // Check GetHasSpell as long as we are not silenced, and have right modifier, and + // the object is seen (this is a backup for it!) + if(!GlobalSilenceSoItems && + (!iRequirement || GlobalSpellAbilityModifier >= iRequirement) && + GetHasSpell(iSpellID) && + (iLocation || GetObjectSeen(oTarget))) + { + // Make sure it is a spell, not an ability + if(iRequirement > FALSE) + { + // Attempt Concentration Check (Casting a spell, not an item) + if(AI_AttemptConcentrationCheck(oTarget)) return TRUE; + } + else + { + // Turn off all modes - remember, we can't use expertise with spellcasting! + AI_SetMeleeMode(); + } + // Set time stop stored to this spell. + AI_SetTimeStopStored(iSpellID); + // 9: "[DCR:Casting] NormalSpell [ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation) + DebugActionSpeakByInt(9, oTarget, iSpellID, IntToString(iLocation)); + // We turn off hiding/searching + AI_ActionTurnOffHiding(); + // Equip the best shield we have + AI_EquipBestShield(); + // Note: 1.3 fix. Action Cast At Object will be used if we can see + // the target, even if it is a location spell + if(GetObjectSeen(oTarget)) + { + // aim at the object directly! + // - See 1.3 fix below. Basically, this should use Meta Magic normally + ActionCastSpellAtObject(iSpellID, oTarget); + } + // If location... + else //if(iLocation) + { + // Fire ActionSpellAtLocation at the given location + // 1.3 fix - Until ActionCastSpellAtLocation works with METAMAGIC + // it will cheat-cast, and decrement the spell by one, with no metamagic. + ActionCastSpellAtLocation(iSpellID, GetLocation(oTarget), METAMAGIC_NONE, TRUE); + DecrementRemainingSpellUses(OBJECT_SELF, iSpellID); + } + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(nTalent)) + { + ActionDoCommand(AI_SetItemTalentValue(nTalent)); + } + // Alway stop - we use else here, so we always do an action! :-D + return TRUE; + } + // 2. Cast from potions, or items! + // This is made simpler by adding in iItemTalentValue and iPotionTalentValue + } + // Basic items - Wands, Scrolls that might pop up - Potions too + if(iItemTalentValue == iSpellID || + iPotionTalentValue == iSpellID) + { + if(AI_ActionCastItemEqualTo(oTarget, iSpellID, iLocation)) + { + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(nTalent)) + { + ActionDoCommand(AI_SetItemTalentValue(nTalent)); + } + return TRUE; + } + } + return FALSE; +} +// This will cast the shadow conjuration/protection version of the spell, always if they have it. +// 1. If they have the spell normally (Using the iUpperSpell spell) +// 2. If they have an item with the spell +// - We always attack with a bow at ranged, but may attack normally after the spell. +// - If nTalent is 0, we do not check items. +// - If iRequirement is 0, it is considered innate. +// - Imput iItemTalentValue to check item talents. +int AI_ActionCastSubSpell(int iSubSpell, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1) +{ + // 1. We need nTalent to be over 0. -1 means an invalid spell for that talent, + // IE no spell for that talent + // - If iRequirement is 0, we consider it innate and no talent category for some + // reason. + if(nTalent > i0 || !iRequirement) + { + // Check GetHasSpell as long as we are not silenced, and have right modifier, and + // the object is seen (this is a backup for it!) + if(!GlobalSilenceSoItems && + (!iRequirement || GlobalSpellAbilityModifier >= iRequirement) && + GetHasSpell(iSubSpell) && + (iLocation || GetObjectSeen(oTarget))) + { + // Make sure it is a spell, not an ability + if(iRequirement > FALSE) + { + // Attempt Concentration Check (Casting a spell, not an item) + if(AI_AttemptConcentrationCheck(oTarget)) return TRUE; + } + else + { + // Turn off all modes - remember, we can't use expertise with spellcasting! + AI_SetMeleeMode(); + } + // Set time stop stored to this spell. + AI_SetTimeStopStored(iSubSpell); + // 11: "[DCR:Casting] SubSpecialSpell. [ID] " + IntToString(iSubSpell) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation) + DebugActionSpeakByInt(11, oTarget, iSubSpell, IntToString(iLocation)); + // We turn off hiding/searching + AI_ActionTurnOffHiding(); + // Equip the best shield we have + AI_EquipBestShield(); + // See 1.3 fix notes about metamagic not being used correctly with + // cast spell at location. + if(GetObjectSeen(oTarget)) + { + // Aim at the object directly! + ActionCastSpellAtObject(iSubSpell, oTarget, METAMAGIC_ANY, TRUE); + } + // If location... + else// if(iLocation) + { + // Fire ActionSpellAtLocation at the given location + ActionCastSpellAtLocation(iSubSpell, GetLocation(oTarget), METAMAGIC_NONE, TRUE); + } + // Decrement + DecrementRemainingSpellUses(OBJECT_SELF, iSubSpell); + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(nTalent)) + { + ActionDoCommand(AI_SetItemTalentValue(nTalent)); + } + // Alway stop - we use else here, so we always do an action! :-D + return TRUE; + } + // 2. Cast from potions, or items! + // This is made simpler by adding in iItemTalentValue and iPotionTalentValue + } + // Basic items - Wands, Scrolls that might pop up - Potions too + if(iItemTalentValue == iSubSpell || + iPotionTalentValue == iSubSpell) + { + if(AI_ActionCastItemEqualTo(oTarget, iSubSpell, iLocation)) + { + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(nTalent)) + { + ActionDoCommand(AI_SetItemTalentValue(nTalent)); + } + return TRUE; + } + } + return FALSE; +} +// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical): +// 0. If d100() is <= iRandom. +// 1. If they have the spell normally +// 2. If they have an item with the spell. +// 3. If they have a potion of the right type. +// - If we are at range from nearest enemy, we attack with a ranged weapon, else do nothing more. +// - If nTalent is -1, we do not check items. +// - Sets GlobalLastSpellValid to iSpellID if we can cast it, but don't randomly. +// Then you can use AI_ActionCastBackupRandomSpell to see if we can cast it later. +int AI_ActionCastSpellRandom(int iSpellID, int nTalent, int iRandom, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1) +{ + // 1. We need nTalent to be over 0. -1 means an invalid spell for that talent, + // IE no spell for that talent + // - If iRequirement is 0, we consider it innate and no talent category for some + // reason. + if(nTalent > i0 || !iRequirement) + { + // Check GetHasSpell as long as we are not silenced, and have right modifier, and + // the object is seen (this is a backup for it!) + if(!GlobalSilenceSoItems && + (!iRequirement || GlobalSpellAbilityModifier >= iRequirement) && + GetHasSpell(iSpellID) && + (iLocation || GetObjectSeen(oTarget))) + { + if(d100() <= iRandom + GlobalRandomCastModifier) + { + // Make sure it is a spell, not an ability + if(iRequirement > FALSE) + { + // Attempt Concentration Check (Casting a spell, not an item) + if(AI_AttemptConcentrationCheck(oTarget)) return TRUE; + } + else + { + // Turn off all modes - remember, we can't use expertise with spellcasting! + AI_SetMeleeMode(); + } + // Set time stop stored to this spell. + AI_SetTimeStopStored(iSpellID); + // 12: "[DCR:Casting] NormalRandomSpell. [ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation) + DebugActionSpeakByInt(12, oTarget, iSpellID, IntToString(iLocation)); + // We turn off hiding/searching + AI_ActionTurnOffHiding(); + // Equip the best shield we have + AI_EquipBestShield(); + // Note: 1.3 fix. Action Cast At Object will be used if we can see + // the target, even if it is a location spell + if(GetObjectSeen(oTarget)) + { + // aim at the object directly! + // - See 1.3 fix below. Basically, this should use Meta Magic normally + ActionCastSpellAtObject(iSpellID, oTarget); + } + // If location... + else //if(iLocation) + { + // Fire ActionSpellAtLocation at the given location + // 1.3 fix - Until ActionCastSpellAtLocation works with METAMAGIC + // it will cheat-cast, and decrement the spell by one, with no metamagic. + ActionCastSpellAtLocation(iSpellID, GetLocation(oTarget), METAMAGIC_NONE, TRUE); + DecrementRemainingSpellUses(OBJECT_SELF, iSpellID); + } + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(nTalent)) + { + ActionDoCommand(AI_SetItemTalentValue(nTalent)); + } + // Alway stop - we use else here, so we always do an action! :-D + return TRUE; + } + else + { + // Don't use acid fog as this (Spell 0). If we have one already set, + // this is a worse spell :-) + AI_SetBackupCastingSpellValues(iSpellID, nTalent, oTarget, iLocation, iRequirement, iItemTalentValue, iPotionTalentValue); + + // Always return FALSE. + return FALSE; + } + } + // 2. Cast from potions, or items! + // This is made simpler by adding in iItemTalentValue and iPotionTalentValue + } + // Basic items - Wands, Scrolls that might pop up. Potions too. + if(iItemTalentValue == iSpellID || + iPotionTalentValue == iSpellID) + { + if(d100() <= iRandom + GlobalRandomCastModifier) + { + if(AI_ActionCastItemEqualTo(oTarget, iSpellID, iLocation)) + { + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(nTalent)) + { + ActionDoCommand(AI_SetItemTalentValue(nTalent)); + } + return TRUE; + } + } + else + { + // Don't use acid fog as this (Spell 0). If we have one already set, + // this is a worse spell :-) + AI_SetBackupCastingSpellValues(iSpellID, nTalent, oTarget, iLocation, iRequirement, iItemTalentValue, iPotionTalentValue); + + // Always return FALSE. + return FALSE; + } + } + return FALSE; +} +// This will used a stored GlobalLastSpellValid to see if it should cast that +// spell (or attempt to!) as a backup. Uses stored targets from when it did know +// it was valid. +int AI_ActionCastBackupRandomSpell() +{ + // Need a valid spell + if(GlobalLastSpellValid > FALSE) + { + object oTarget; + int iSpell, iTalent, iLocation, iItem, iPotion, iRequirement; + iSpell = GlobalLastSpellValid; + // Delete again for other castings + GlobalLastSpellValid = FALSE; + // 13: "[DCR:Casting] Backup spell caught: " + IntToString(iSpell) + DebugActionSpeakByInt(13, OBJECT_INVALID, iSpell); + + // Get things from GLOBAL_LAST_SPELL_INFORMATION1 to GLOBAL...ATION5 + int iCnt = i1; + // GET... + // talent + iTalent = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt)); + // target + iCnt++; + oTarget = GetAIObject(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt)); + // location + iCnt++; + iLocation = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt)); + // reqirement + iCnt++; + iRequirement = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt)); + // item + iCnt++; + iItem = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt)); + // potion + iCnt++; + iPotion = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt)); + + // Cast spell at 100% chance, and innate (already checked iRequirement) + // - Should cast. + if(AI_ActionCastSpell(iSpell, iTalent, oTarget, iRequirement, iLocation, iItem, iPotion)) return TRUE; + } + return FALSE; +} + +int AI_ActionCastSummonSpell(int iThingID, int iRequirement = 0, int iSummonLevel = 0) +{ + // Feat + if(iRequirement == iM1) + { + if(GetHasFeat(iThingID)) + { + // 14: "[DCR:Feat] [ID] " + IntToString(iFeat) + " [Enemy] " + GetName(oObject) + DebugActionSpeakByInt(14, OBJECT_SELF, iThingID); + // talent tFeat doesn't work. + ActionUseFeat(iThingID, OBJECT_SELF); + SetAIInteger(AI_LAST_SUMMONED_LEVEL, iSummonLevel); + return TRUE; + } + } + else if(SpellAllies) + { + // Check GetHasSpell as long as we are not silenced, and have right modifier, and + // the object is seen (this is a backup for it!) + if(!GlobalSilenceSoItems && + (iRequirement == FALSE || GlobalSpellAbilityModifier >= iRequirement) && + GetHasSpell(iThingID)) + { + // Make sure it is a spell, not an ability + if(iRequirement > FALSE) + { + // Attempt Concentration Check (Casting a spell, not an item) + if(AI_AttemptConcentrationCheck(GlobalSpellTarget)) return TRUE; + } + else + { + // Turn off all modes - remember, we can't use expertise with spellcasting! + AI_SetMeleeMode(); + } + // Set time stop stored to this spell. + AI_SetTimeStopStored(iThingID); + // 9: "[DCR:Casting] NormalSpell [ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation) + DebugActionSpeakByInt(9, GlobalSpellTarget, iThingID); + // We turn off hiding/searching + AI_ActionTurnOffHiding(); + // Equip the best shield we have + AI_EquipBestShield(); + // Fire ActionSpellAtLocation at the given location + // 1.3 fix - Until ActionCastSpellAtLocation works with METAMAGIC + // it will cheat-cast, and decrement the spell by one, with no metamagic. + ActionCastSpellAtLocation(iThingID, GlobalSummonLocation, METAMAGIC_NONE, TRUE); + DecrementRemainingSpellUses(OBJECT_SELF, iThingID); + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(SpellAllies)) + { + ActionDoCommand(AI_SetItemTalentValue(SpellAllies)); + } + // Alway stop - we use else here, so we always do an action! :-D + return TRUE; + } + } + // Basic items - Wands, Scrolls that might pop up + else if(ItemAllies == iThingID) + { + // We need to get what one is actually the talent number :-D + // This is actually faster, as we know that iSpellID equals one of them :-D + // Set to local integers + int nTalent, iReturn; + if(GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES)) == iThingID) + { + nTalent = ItemAllies; + } + // Check for valid talent (1+) + if(nTalent >= i1) + { + // Apply EffectCutsceneImmobilize. It only removes ones with no spell ID anyway :-D + AI_SpecialActionApplyItem(); + + talent tBestOfIt = GetCreatureTalentBest(nTalent, i20); + // JUST to make sure! + if(GetIsTalentValid(tBestOfIt) && + GetIdFromTalent(tBestOfIt) == iThingID && + GetTypeFromTalent(tBestOfIt) == TALENT_TYPE_SPELL) + { + AI_SetTimeStopStored(iThingID); + // 7: "[DCR:Casting] Talent(item) [TalentID] " + IntToString(GetIdFromTalent(tBestOfIt)) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation) + DebugActionSpeakByInt(7, GlobalSpellTarget, GetIdFromTalent(tBestOfIt)); + // Equip the best shield we have + AI_EquipBestShield(); + // Use this only for items, so we should not have the spell. + ActionUseTalentAtLocation(tBestOfIt, GlobalSummonLocation); + iReturn = TRUE; + } + // remove the EffectCutsceneImmobilize, if so. + AI_SpecialActionRemoveItem(); + // Lasts...recheck items + if(AI_GetSpellCategoryHasItem(nTalent)) + { + ActionDoCommand(AI_SetItemTalentValue(nTalent)); + } + return iReturn; + } + } + // No summon created + return FALSE; +} + + +// If they have the feat, they will use it on the target, and return TRUE +// * iFeat - Feat ID to use +// * oObject - object to use it on (Can't target locations in the AI - not worth it) +// * iSummonLevel - when using a summoning feat (EG: blackguard undead) use a number here. If false, its ignored +int AI_ActionUseFeatOnObject(int iFeat, object oObject = OBJECT_SELF) +{ + if(GetHasFeat(iFeat) && GetIsObjectValid(oObject)) + { + if(!GetHasFeatEffect(iFeat, oObject)) + { + // 14: "[DCR:Feat] [ID] " + IntToString(iFeat) + " [Enemy] " + GetName(oObject) + DebugActionSpeakByInt(14, oObject, iFeat); + // We turn off hiding/searching + AI_ActionTurnOffHiding(); + ActionUseFeat(iFeat, oObject); + if(oObject == OBJECT_SELF) + { + if(GetIsObjectValid(GlobalMeleeTarget)) ActionAttack(GlobalMeleeTarget); + } + return TRUE; + } + } + return FALSE; +} +// If they have nFeat, they cheat-cast nSpell at oTarget. +// - This is a workaround, as some epic spells, for some reason, won't work with +// a standard ActionUseFeatOnObject()! Dammit. Beta 3 addition. +int AI_ActionUseEpicSpell(int nFeat, int nSpell, object oTarget = OBJECT_SELF) +{ + if(GetHasFeat(nFeat)) + { + // 14: "[DCR:Feat] [ID] " + IntToString(nFeat) + " [Enemy] " + GetName(oTarget) + DebugActionSpeakByInt(14, oTarget, nFeat); + // We turn off hiding/searching + AI_ActionTurnOffHiding(); + // Cheat cast the spell + ActionCastSpellAtObject(nSpell, oTarget, METAMAGIC_NONE, TRUE); + // Decrement casting of it. + DecrementRemainingFeatUses(OBJECT_SELF, nFeat); + return TRUE; + } + return FALSE; +} + +// This attempts to check the talent TALENT_CATEGORY_HARMFUL_RANGED, 2, which +// holds grenades. Easy checks. +// TRUE if they fire a grenade. +int AI_AttemptGrenadeThrowing(object oTarget) +{ + int iReturn = FALSE; + // Check if the items is a grenade: + // SPELL_GRENADE_ACID - 1d6 Acid Damge/Target, or 1 splash. + // SPELL_GRENADE_CALTROPS - Up to 25 normal damage, at 1 Damage/round to an AOE + // SPELL_GRENADE_CHICKEN - Chicken - fires a chicken and fireball + // SPELL_GRENADE_CHOKING - Stinking cloud type effect, dazes on fort save. + // SPELL_GRENADE_FIRE - 1d6 fire damage/target, or 1 splash + // SPELL_GRENADE_HOLY - 2d4 Divine Damage to undead, 1 to undead in splash + // SPELL_GRENADE_TANGLE - Entangles spell target, reflex save. + // SPELL_GRENADE_THUNDERSTONE - Deafens against a DC 15 fort save, AOE. + + // 744 Grenade_FireBomb - Big fire 'nade. Has an AOE after + // 745 Grenade_AcidBomb - Big Acid 'nade. Has an AOE after damage + + if((ItemHostRanged >= SPELL_GRENADE_FIRE && + ItemHostRanged <= SPELL_GRENADE_CALTROPS) || + ItemHostRanged == 744 || ItemHostRanged == 745) + { + // We have a valid item grenade. We then throw it (or attempt to!) + // - Check holy grenade not firing Versus non-undead + if(ItemHostRanged == SPELL_GRENADE_HOLY && + GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) + { + // Stop as they are not undead + return FALSE; + } + + // We fire the spell at the target if they are seen + // - If SpellTargetSeen is TRUE, fire at them + int iLocation = FALSE; + if(!GlobalSeenSpell) + { + iLocation = TRUE; + } + + // Apply EffectCutsceneImmobilize. It only removes ones with no spell ID anyway :-D + AI_SpecialActionApplyItem(); + + talent tBestOfIt = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_RANGED, i20); + // JUST to make sure! + if(GetIsTalentValid(tBestOfIt) && + GetIdFromTalent(tBestOfIt) == ItemHostRanged) + { + // 15: "[DCR:Casting] Grenade [ID] " + IntToString(ItemHostRanged) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation) + DebugActionSpeakByInt(15, GlobalSpellTarget, ItemHostRanged, IntToString(iLocation)); + // Equip the best shield we have + AI_EquipBestShield(); + // Use this only for items, so we should not have the spell. + if(iLocation) + { + ActionUseTalentAtLocation(tBestOfIt, GetLocation(oTarget)); + } + else //if(!GetObjectSeen(GlobalSpellTarget)) // Should be seen - checked before. + { + ActionUseTalentOnObject(tBestOfIt, oTarget); + } + iReturn = TRUE; + } + // remove the EffectCutsceneImmobilize, if so. + AI_SpecialActionRemoveItem(); + + // Lasts...recheck items + // TALENT_CATEGORY_HARMFUL_RANGED is always checked for items. + ActionDoCommand(AI_SetItemTalentValue(TALENT_CATEGORY_HARMFUL_RANGED)); + } + return iReturn; +} +/*:://///////////////////////////////////////////// +//:: Name: GetBestSpontaeousHealingSpell +//:://///////////////////////////////////////////// +// This will return the best spontaeous healing spell, so: +// - It uses just normal GetHasSpell for the clerical healing spells. +// - It gets set up at the start to the global "GlobalBestSpontaeousHealingSpell" +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_GetBestSpontaeousHealingSpell() +{ + if(GetHasSpell(SPELL_CURE_CRITICAL_WOUNDS)) + { + return SPELL_CURE_CRITICAL_WOUNDS; + } + else if(GetHasSpell(SPELL_CURE_SERIOUS_WOUNDS)) + { + return SPELL_CURE_SERIOUS_WOUNDS; + } + else if(GetHasSpell(SPELL_CURE_MODERATE_WOUNDS)) + { + return SPELL_CURE_MODERATE_WOUNDS; + } + else if(GetHasSpell(SPELL_CURE_LIGHT_WOUNDS)) + { + return SPELL_CURE_LIGHT_WOUNDS; + } + else if(GetHasSpell(SPELL_CURE_MINOR_WOUNDS)) + { + return SPELL_CURE_MINOR_WOUNDS; + } + // False = no spell + return FALSE; +} + +/*:://///////////////////////////////////////////// +//:: Name: AI_SetUpUs +//:://///////////////////////////////////////////// + This sets up US, the user! :-) + - Determines class to use, dragon or not. + - And some other things that don't need to check allies/enemies for. + - Intelligence and so on, global effects of us and so on ;-) +//:://///////////////////////////////////////////*/ +void AI_SetUpUs() +{ + int iLastSpellType, nClass1, nClass2, nClass3, nLevel1, nLevel2, nLevel3, + nState1, nState2, nState3, nUseClass, iCurrent; + object oSummon; + float fTotal; + // We set up what intelligence we have (level). See OnSpawn for more info + // Default is 10, top 10, bottom 1. + GlobalIntelligence = GetBoundriedAIInteger(AI_INTELLIGENCE, i10, i10, i1); + // Checks the 3 classes, and returns one of them randomly based on how many they + // have in that level compared to the other 2. + GlobalOurHitDice = GetHitDice(OBJECT_SELF); + GlobalThisArea = GetArea(OBJECT_SELF); + GlobalOurRace = GetRacialType(OBJECT_SELF); + // HP + GlobalOurCurrentHP = GetCurrentHitPoints(); + GlobalOurMaxHP = GetMaxHitPoints(); + // Use Floats to get Decimal places. + GlobalOurPercentHP = AI_GetPercentOf(GlobalOurCurrentHP, GlobalOurMaxHP); + GlobalOurSize = GetCreatureSize(OBJECT_SELF); + // AI - just normal. More added/subtracted be;pw + GlobalOurAC = GetAC(OBJECT_SELF); + switch(GlobalOurSize) + { + case CREATURE_SIZE_TINY: GlobalOurAC += i2; break; + case CREATURE_SIZE_SMALL: GlobalOurAC += i1; break; + case CREATURE_SIZE_LARGE: GlobalOurAC -= i1; break; + case CREATURE_SIZE_HUGE: GlobalOurAC -= i2; break; + } + GlobalOurAppearance = GetAppearanceType(OBJECT_SELF); + GlobalOurGoodEvil = GetAlignmentGoodEvil(OBJECT_SELF);// Used for alignment prot. Spells. + // Weapons (in places) or objects :-P + GlobalLeftHandWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND); + GlobalRightHandWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND); + GlobalOurBaseAttackBonus = GetBaseAttackBonus(OBJECT_SELF); + // Spell Ranged Attacking + if(GetSpawnInCondition(AI_FLAG_COMBAT_LONGER_RANGED_SPELLS_FIRST, AI_COMBAT_MASTER)) + { + SRA = TRUE; + } + // Set up the extra % to random cast + GlobalRandomCastModifier = GlobalIntelligence * i2; + // - 2% extra at 1, 20% at 10 :-) + + // Set if we are a global buffer + GlobalWeAreBuffer = GetSpawnInCondition(AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS, AI_COMBAT_MASTER); + + // Set if we only use items + GlobalSilenceSoItems = AI_GetAIHaveEffect(GlobalEffectSilenced); + + // If we have any of the silent feats, epic, we ignore any silence we have. + if(GlobalSilenceSoItems) + { + if(GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_1) || + GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_2) || + GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_3)) + { + GlobalSilenceSoItems = FALSE; + } + } + + // Our reach is the distance we can immediantly attack, normally. + GlobalOurReach = IntToFloat(GlobalOurSize * i4) + f1; + // Sets up the class to use. + fTotal = IntToFloat(GlobalOurHitDice); + nClass1 = GetClassByPosition(i1); + nClass2 = GetClassByPosition(i2); + nClass3 = GetClassByPosition(i3); + nLevel1 = GetLevelByClass(nClass1); + nLevel2 = GetLevelByClass(nClass2); + nLevel3 = GetLevelByClass(nClass3); + // Set up how much % each class occupies. + nState1 = FloatToInt((IntToFloat(nLevel1) / fTotal) * i100); + nState2 = FloatToInt((IntToFloat(nLevel2) / fTotal) * i100) + nState1; + nState3 = FloatToInt((IntToFloat(nLevel3) / fTotal) * i100) + nState2; + // Randomise the % we pick + nUseClass = d100(); + // Set the class, and that classes level. + if(nUseClass <= nState1) + { + GlobalOurChosenClass = nClass1; + GlobalOurChosenClassLevel = nLevel1; + } + else if(nUseClass > nState1 && nUseClass <= nState2) + { + GlobalOurChosenClass = nClass2; + GlobalOurChosenClassLevel = nLevel2; + } + else + { + GlobalOurChosenClass = nClass3; + GlobalOurChosenClassLevel = nLevel3; + } + // Intelligence based spellcaster. + if(GlobalOurChosenClass == CLASS_TYPE_WIZARD) + { + GlobalSpellAbilityModifier = GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE); + GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_INTELLIGENCE); + } + // Wisdom based spellcaster + else if(GlobalOurChosenClass == CLASS_TYPE_DRUID || + GlobalOurChosenClass == CLASS_TYPE_CLERIC) + { + GlobalSpellAbilityModifier = GetAbilityScore(OBJECT_SELF, ABILITY_WISDOM); + GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_WISDOM); + } + // Charisma + else if(GlobalOurChosenClass == CLASS_TYPE_BARD || + GlobalOurChosenClass == CLASS_TYPE_SORCERER) + { + // Summoning specials (and some others). If we are a bard/sorceror, it means + // we cast 1 from X spells, not just "Only got that spell". + GlobalWeAreSorcerorBard = TRUE; + // Charisma based spellcaster + GlobalSpellAbilityModifier = GetAbilityScore(OBJECT_SELF, ABILITY_CHARISMA); + GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_CHARISMA); + } + else // Monster + { + // - We set ability modifier to 25, so that they cast all spells + // and monster abilties + GlobalSpellAbilityModifier = i25; + GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_CHARISMA); + } + // Set up GlobalSaveStupidity, 10 - Intelligence + // 0 is better then 10! Bascially, if they are immune (EG: Massive fortitude + // fighter VS death save) then taking 10 from thier save stat means a lower + // intelligence caster will fire it against immune beings. + // - Also used in AOE checking. + GlobalSaveStupidity = i10 - GlobalIntelligence; + // Spontaeous healing spell + GlobalBestSpontaeousHealingSpell = AI_GetBestSpontaeousHealingSpell(); + + // Set up SR roll + // - 20 + Class level + 2 for spell penetration, +4 for greater. + // - We always take it as a 20 - but we set this for a huge amount. + // NOTE: we check HD only - because of monster abilities. + GlobalSpellPenetrationRoll = GlobalOurHitDice + i20; + // Check for feats + if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION)) + { + GlobalSpellPenetrationRoll += i6; + } + else if(GetHasFeat(FEAT_GREATER_SPELL_PENETRATION)) + { + GlobalSpellPenetrationRoll += i4; + } + else if(GetHasFeat(FEAT_SPELL_PENETRATION)) + { + GlobalSpellPenetrationRoll += i2; + } + // Summon checking (special) + // Used for summoned creatures. + oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED); + if(GetIsObjectValid(oSummon) && !GetIsDead(oSummon)) + { + // If valid, we can use the level of the last cast to check if valid. + int iCurrentSummonLevel = GetAIInteger(AI_LAST_SUMMONED_LEVEL); + // - Never replace epic, or elemental sparm, or balors (10, 11/12) + // - Never replace sorcerors or bards + if(!GlobalWeAreSorcerorBard && iCurrentSummonLevel <= i9) + { + // We check thier HP. If they are under x6% and under 30HP, we may summon + // a summon over this one that exsists. + iCurrent = GetCurrentHitPoints(oSummon); + if(((iCurrent * i6) < GetMaxHitPoints(oSummon)) && (iCurrent <= i30)) + { + // Make it -1, so that we will say, summon a level 5 summon + // over a damaged level 6, but never a level 2 summon in replacement. + GlobalCanSummonSimilarLevel = iCurrentSummonLevel - i1; + } + } + // If we have not set GlobalCanSummonSimilarLevel, we set it so we + // should not summon anything at all! + if(!GlobalCanSummonSimilarLevel) + { + GlobalCanSummonSimilarLevel = i100; + } + } + else + { + // Reset to 0, false, to summon any monster + DeleteAIInteger(AI_LAST_SUMMONED_LEVEL); + GlobalCanSummonSimilarLevel = FALSE; + } + + // Right: + // - If we have valid category, we will set it to the talent value. + // - We then use this in the spells, tightens up some things :-) + // - Use 16 as another "any other" category. This isn't checked for items/ + + // Sets each one to TRUE if we have any of that category (and a spell) + int iLocalSpellInteger = GetLocalInt(OBJECT_SELF, AI_VALID_SPELLS); + // Any? + if(iLocalSpellInteger & AI_VALID_OTHER_SPELL) SpellOtherSpell = i23;// New one + // Conditional + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_AREAEFFECT) SpellConSinTar = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE; + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_SINGLE) SpellConAre = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT; + // Enchancement + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_AREAEFFECT) SpellEnhAre = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT; + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SELF) SpellEnhSelf = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF; + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SINGLE) SpellEnhSinTar = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE; + // Other + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_OBTAIN_ALLIES) SpellAllies = TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES; + if(iLocalSpellInteger & AI_VALID_TALENT_PERSISTENT_AREA_OF_EFFECT) SpellAura = TALENT_CATEGORY_PERSISTENT_AREA_OF_EFFECT; + // Protection + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_PROTECTION_AREAEFFECT) SpellProAre = TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT; + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_PROTECTION_SELF) SpellProSelf = TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF; + if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_PROTECTION_SINGLE) SpellProSinTar = TALENT_CATEGORY_BENEFICIAL_PROTECTION_SINGLE; + // Hostile/Harmful. + if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_AREAEFFECT_DISCRIMINANT) SpellHostAreaDis = TALENT_CATEGORY_HARMFUL_AREAEFFECT_DISCRIMINANT; + if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_AREAEFFECT_INDISCRIMINANT) SpellHostAreaInd = TALENT_CATEGORY_HARMFUL_AREAEFFECT_INDISCRIMINANT; + if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_RANGED) SpellHostRanged = TALENT_CATEGORY_HARMFUL_RANGED; + if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_TOUCH) SpellHostTouch = TALENT_CATEGORY_HARMFUL_TOUCH; + + // Breath weapon + if(iLocalSpellInteger & AI_VALID_TALENT_DRAGONS_BREATH) SpellHostBreath = TALENT_CATEGORY_DRAGONS_BREATH; + + // ANY spells valid? + if(iLocalSpellInteger & AI_VALID_ANY_SPELL) SpellAnySpellValid = TRUE; + + // Hostile feats + if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_MELEE) ValidFeats = TRUE; + + // Now, what about, say, ITEMS?!?! + // Items/Spells to reduce lag. + + // If no items, then we will set iLastSpellType to 0 so that none are reset + if(!GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_ITEMS, AI_OTHER_MASTER)) + { +//*1*/ SpellHostAreaDis, ItemHostAreaDis, + ItemHostAreaDis = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_AREAEFFECT_DISCRIMINANT)); +//*2*/ SpellHostRanged, ItemHostRanged, + ItemHostRanged = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_RANGED)); +//*3*/ SpellHostTouch, ItemHostTouch, + ItemHostTouch = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_TOUCH)); +//*6*/ SpellConAre, ItemConAre, + ItemConAre = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT)); +//*7*/ SpellConSinTar, ItemConSinTar, + ItemConSinTar = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE)); +//*8*/ SpellEnhAre, ItemEnhAre, + ItemEnhAre = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT)); +//*9*/ SpellEnhSinTar, ItemEnhSinTar, + ItemEnhSinTar = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE)); +//*10*/ SpellEnhSelf, ItemEnhSelf, + ItemEnhSelf = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF)); +//*11*/ SpellHostAreaInd, ItemHostAreaInd, + ItemHostAreaInd = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_AREAEFFECT_INDISCRIMINANT)); +//*12*/ SpellProSelf, ItemProSelf, + ItemProSelf = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF)); +//*13*/ SpellProSinTar, ItemProSinTar, + ItemProSinTar = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_PROTECTION_SINGLE)); +//*14*/ SpellProAre, ItemProAre, + ItemProAre = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT)); +//*15*/ SpellAllies, ItemAllies, + ItemAllies = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES)); +//*18*/ PotionCon, +//*20*/ PotionPro, +//*21*/ PotionEnh, + // These are general talents, Always set because we can use these parrallel to spells. + tPotionCon = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_POTION, i20); + tPotionPro = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_PROTECTION_POTION, i20); + tPotionEnh = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_POTION, i20); + // Potions. We do use talents for these to be safe! + // No worries if it is 0 or nothing, we compare them not to acid fog ever. + PotionCon = GetIdFromTalent(tPotionCon); + PotionPro = GetIdFromTalent(tPotionPro); + PotionEnh = GetIdFromTalent(tPotionEnh); + } + + // We got any potions? + if(PotionCon || PotionPro || PotionEnh) GobalPotionsValid = TRUE; + + // We got any items? + if(ItemAllies || ItemConSinTar || ItemConAre || ItemProSelf || ItemProSinTar || + ItemProAre || ItemEnhSelf || ItemEnhSinTar || ItemEnhAre || ItemHostAreaDis || + ItemHostAreaInd || ItemHostRanged || ItemHostTouch) GobalOtherItemsValid = TRUE; + + // Healing Kits + int iHealLeft = GetAIInteger(AI_VALID_HEALING_KITS); + if(iHealLeft) + { + // Get the kit + GlobalHealingKit = GetAIObject(AI_VALID_HEALING_KIT_OBJECT); + // Oh, if we don't have one, re-set them, and only them. + if(!GetIsObjectValid(GlobalHealingKit) && iHealLeft >= i2) + { + SetAIInteger(RESET_HEALING_KITS, TRUE); + ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF); + } + } +} +/*:://///////////////////////////////////////////// +//:: Name: AI_GetNearbyFleeObject +//:://///////////////////////////////////////////// + This returns an object, not seen not heard ally, who we + might flee to. Uses a loop, and runs only when we are going to flee for sure. +//:://///////////////////////////////////////////*/ +object AI_GetNearbyFleeObject() +{ + object oReturn, oGroup, oEndReturn; + int iCnt; + string sCheck; + if(GetSpawnInCondition(AI_FLAG_FLEEING_FLEE_TO_NEAREST_NONE_SEEN, AI_TARGETING_FLEE_MASTER)) + { + oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + OBJECT_SELF, i1, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD, + CREATURE_TYPE_IS_ALIVE, TRUE); + if(GetIsObjectValid(oReturn)) // Need LOS check + { + return oReturn; + } + } + if(GetSpawnInCondition(AI_FLAG_FLEEING_FLEE_TO_OBJECT, AI_TARGETING_FLEE_MASTER)) + { + sCheck = GetLocalString(OBJECT_SELF, AI_FLEE_OBJECT); + if(sCheck != "") + { + // We need to get the nearest of sCheck objects we cannot see nor hear, and + // over 6 meters just in case. + iCnt = i1; + oReturn = GetNearestObjectByTag(sCheck, OBJECT_SELF, iCnt); + while(GetIsObjectValid(oReturn)) + { + // this should be simple enough to break when the object is valid. + if(!GetObjectSeen(oReturn) && !GetObjectHeard(oReturn) && + GetDistanceToObject(oReturn) > f6) // (must be same area) + { + // Stop if valid + return oReturn; + } + iCnt++; + oReturn = GetNearestObjectByTag(sCheck, OBJECT_SELF, iCnt); + } + if(!GetIsObjectValid(oReturn)) + { + // Just get any! + oReturn = GetObjectByTag(sCheck); + if(GetIsObjectValid(oReturn)) + { + return oReturn; + } + } + } + } + // Reset + oReturn = OBJECT_INVALID; + oGroup = OBJECT_INVALID; + // By default: + // At 1-3 INT, we run to the nearest non-seen, non-heard. + // At 4-7, we run to the best ally group, within 35M, or an ally who is +5 our HD. + // At 8+, we run to the best group, in 70M, or an ally who is +8 our HD, and we shout for help (HB) + + if(GlobalIntelligence <= i3) + { + // Don't care if not valid! + oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + OBJECT_SELF, i1, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD, + CREATURE_TYPE_IS_ALIVE, TRUE); + return oReturn; + } + + // Counters ETC + int iBestAllyGroupTotal, iCurrentGroupHD, nCnt, iGroupCnt, IfHigherBreak; + // Set floats + float fMaxCheckForGroup, fMaxGroupRange; + // Check range (Ie people we get near to us, need to be in this range) + fMaxCheckForGroup = 100.0;// 10 tiles + if(GlobalIntelligence >= i8) fMaxCheckForGroup *= i2; // Double check range. + fMaxGroupRange = f15;// Default. No need to change. + + // We break when we have a group totaling some value of our HD... + // It goes up as intelligence does.(IE 4, 5, 6, 7, 8, 9 or 10 * HD/2 + 1) + IfHigherBreak = GlobalOurHitDice * ((GlobalIntelligence / i2) + i1); + // Note to self: THis means highest intelligence runs futhest away, hopefully smarter. + // Note: Need an acceptable limit. 10 * 20 is 200, max 100 though. + if(IfHigherBreak > i100) IfHigherBreak = i100; + + nCnt = i1;// Start at 1 nearest. + // Nearest ally is got...we use not seen/not heard, not PC and friendly. + // Making it oReturn might return something at the very least. + oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + OBJECT_SELF, nCnt, + CREATURE_TYPE_IS_ALIVE, TRUE, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD); + // Need to be valid, the things we get, and not in X float meters. + while(GetIsObjectValid(oReturn) && GetDistanceToObject(oReturn) <= fMaxCheckForGroup) + { + // Loop the people around him, + iCurrentGroupHD = GetHitDice(oReturn); + iGroupCnt = i1; + oGroup = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + oReturn, iGroupCnt, + CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD); + // Remeber 15M range limit. + while(GetIsObjectValid(oGroup) && + GetDistanceBetween(oReturn, oGroup) <= fMaxGroupRange) + { + iCurrentGroupHD += GetHitDice(oGroup); + // Get next group object + iGroupCnt++; + oGroup = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + oReturn, iGroupCnt, + CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD); + } + // Sets the ally, if got lots of allies. (Or a mass of HD) + if(iCurrentGroupHD > iBestAllyGroupTotal) + { + iBestAllyGroupTotal = iCurrentGroupHD; + oEndReturn = oReturn; + // Break time. + // It is (Int * HD/2 + 1), max 100. Shouldn't be too bad. + if(iCurrentGroupHD >= IfHigherBreak) + { + // Return the object + return oReturn; + } + } + nCnt++; + oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + OBJECT_SELF, nCnt, + CREATURE_TYPE_IS_ALIVE, TRUE, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD); + } + // By default, return nothing (oEndReturn = OBJECT_INVALID unless set in loop) + return oEndReturn; +} +/*:://///////////////////////////////////////////// +//:: Name: AI_CompareTimeStopStored +//:://///////////////////////////////////////////// + TRUE if the spell is one recorded as being cast before in time stop. + - Checks Global "Are we in time stop?" and returns FALSE if not in time stop +//:://///////////////////////////////////////////*/ +int AI_CompareTimeStopStored(int nSpell, int nSpell2 = 0, int nSpell3 = 0, int nSpell4 = 0) +{ + // Set array size is under 0, IE no array, stop. + if(GlobalTimeStopArraySize < i0) return FALSE; + if(GlobalInTimeStop) + { + int iSpell = iM1; + int iCnt; + if(GlobalTimeStopArraySize == i0) + { + GlobalTimeStopArraySize = GetAIInteger(TIME_STOP_LAST_ARRAY_SIZE); + if(GlobalTimeStopArraySize == i0) + { + GlobalTimeStopArraySize = iM1; + } + } + if(GlobalTimeStopArraySize > i0) + { + for(iCnt = i1; iCnt <= GlobalTimeStopArraySize; iCnt++) + { + iSpell = GetAIConstant(TIME_STOP_LAST_ + IntToString(iCnt)); + if(iSpell == nSpell || + iSpell == nSpell2 || + iSpell == nSpell3 || + iSpell == nSpell4) + { + return TRUE; + } + } + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: AI_GetBestFriendyAreaSpellTarget +//:://///////////////////////////////////////////// + Returns the object to the specifications: + Within nRange (float) + The most targets around the creature in nRange, in nSpread. + Can be the caster, of course +//:://///////////////////////////////////////////*/ +/* UNCOMMENT +object AI_GetBestFriendyAreaSpellTarget(float fRange, float fSpread, int nShape) +{ + object oGroupies, oSpellTarget, oTarget; + int iCountOnPerson, iMostOnPerson, nCnt; + location lTarget; + // Will always at least return ourselves. + oSpellTarget = OBJECT_SELF; + nCnt = i1; + // Gets the nearest friend...the loops takes care of range. + oTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(nCnt)); + // Start loop. Checks range here. + while(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= fRange) + { + lTarget = GetLocation(oTarget); + // Starts the count at 0, as first object in shape will also include the target. + iCountOnPerson = i0; + // Reset/Start counting the spread on oTarget. + oGroupies = GetFirstObjectInShape(nShape, fSpread, lTarget); + // If oGroupies is invalid, nothing. + while(GetIsObjectValid(oGroupies)) + { + // Only add one if the person is an friend + if(GetIsFriend(oGroupies)) + { + iCountOnPerson++; + } + oGroupies = GetNextObjectInShape(nShape, fSpread, lTarget); + } + if(iCountOnPerson > iMostOnPerson) + { + iMostOnPerson = iCountOnPerson; + oSpellTarget = oTarget; + } + nCnt++; + oTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(nCnt)); + } + // Will always return self if anything + return oSpellTarget; +} END UNCOMMENT*/ +// If the target will always save against iSaveType, and will take no damage, returns TRUE +// * Target is GlobalSpellTarget. Save is GlobalSpellTargetWill ETC. +// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE +// * iSpellLevel - Level of the spell being cast. +int AI_SaveImmuneSpellTarget(int iSaveType, int iSpellLevel) +{ + if(iSaveType == SAVING_THROW_FORT) + { + // Basic one here. Some addition and comparison. + if(GlobalSpellTargetFort - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE; + } + else if(iSaveType == SAVING_THROW_REFLEX) + { + // Evasion - full damaged saved if the save is sucessful. + if(GetHasFeat(FEAT_EVASION, GlobalSpellTarget) || + GetHasFeat(FEAT_IMPROVED_EVASION, GlobalSpellTarget)) + { + if(GlobalSpellTargetReflex - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE; + } + } + else if(iSaveType == SAVING_THROW_WILL) + { + // Slippery mind has a re-roll. 1.3 - Ignore + if(GlobalSpellTargetWill - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) + { + return TRUE; + } + } + return FALSE; +} +// If the target will always save against iSaveType, and will take no damage, returns TRUE +// * oTarget - who saving against spell. +// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE +// * The save used is GetReflexSavingThrow* ETC. +// * iSpellLevel - Level of the spell being cast. +int AI_SaveImmuneAOE(object oTarget, int iSaveType, int iSpellLevel) +{ + // GlobalSaveStupidity = 10 - Intelligence. Basically, lower intellgence + // will fire spells which may do nothing more often. + + // Return if no level (innate ability normally) + if(iSpellLevel == FALSE) return FALSE; + if(iSaveType == SAVING_THROW_FORT) + { + // Basic one here. Some addition and comparison. + if(GetFortitudeSavingThrow(oTarget) - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE; + } + else if(iSaveType == SAVING_THROW_REFLEX) + { + // Evasion - full damaged saved if the save is sucessful. + if(GetHasFeat(FEAT_EVASION, oTarget) || + GetHasFeat(FEAT_IMPROVED_EVASION, oTarget)) + { + if(GetReflexSavingThrow(oTarget)- GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE; + } + } + else if(iSaveType == SAVING_THROW_WILL) + { + // Slippery mind has a re-roll. We ignore in 1.3 + if(GetWillSavingThrow(oTarget) - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) + { + return TRUE; + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: SpellResistanceImmune +//:://///////////////////////////////////////////// + This checks targets spell resistance. If our level + 20 is below thier + resistance, the spell won't affect them. +//::////////////////////////////////////////////*/ +int AI_SpellResistanceImmune(object oTarget) +{ + // Check the targets spell resistance VS our GlobalSpellPenetrationRoll + // GlobalSpellPenetrationRoll = Our Hit Dice + Special help feats. + if(GetSpellResistance(oTarget) >= GlobalSpellPenetrationRoll) + { + return TRUE; + } + // Note: REmoved monk feat, and spell resistance spell, but not sure if + // it checks alignment-orientated SR's... +/* if(GlobalOurGoodEvil != ALIGNMENT_NEUTRAL) + { + // Alinment protection (highest) is a SR 25 + if(GlobalOurGoodEvil == ALIGNMENT_GOOD) + { + if(GetHasSpellEffect(SPELL_UNHOLY_AURA)) + { + if(i25 >= GlobalSpellPenetrationRoll) return TRUE; + } + } + else if(GlobalOurGoodEvil == ALIGNMENT_EVIL) + { + if(GetHasSpellEffect(SPELL_HOLY_AURA)) + { + if(i25 >= GlobalSpellPenetrationRoll) return TRUE; + } + } + } */ + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: GetSpellLevelEffect +//:://///////////////////////////////////////////// + This returns a number, 1-4. This number is the levels + of spell they will be totally immune to. +//::////////////////////////////////////////////*/ +int AI_GetSpellLevelEffect(object oTarget) +{ + int iNatural = GetLocalInt(oTarget, AI_SPELL_IMMUNE_LEVEL); + // Stop here, if natural is over 4 + if(iNatural > i4) return iNatural; + + // Big globe affects 4 or lower spells + if(GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, oTarget) || iNatural >= i4) + return i4; + // Minor globe is 3 or under + if(GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, oTarget) || + // Shadow con version + GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, oTarget) || + iNatural >= i3) + return i3; + // 2 and under is ethereal visage. + if(GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, oTarget) || iNatural >= i2) + return i2; + // Ghostly Visarge affects 1 or 0 level spells, and any spell immunity. + if(GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, oTarget) || iNatural >= i1 || + // Or shadow con version. + GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE, oTarget)) + return i1; + // Return iNatural, which is 0-9 + return FALSE; +} + +int AI_GetSpellLevelEffectAOE(object oTarget, int iLevel = 9) +{ + // Return if no level (innate ability normally) + if(iLevel == FALSE) return FALSE; + // Checks any NPC natural total spell immunities (like globes, but on hides) + // - On PC's, the local will return 0. this saves an extra !GetIsPC check. + if(iLevel <= GetLocalInt(oTarget, AI_SPELL_IMMUNE_LEVEL)) + { + return TRUE; + } + // Big globe affects 4 or lower spells + if(iLevel <= i4 && GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, oTarget)) + { + return TRUE; + } + // Minor globe is 3 or under + if(iLevel <= i3 && (GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, oTarget) || + // Shadow con version + GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, oTarget))) + { + return TRUE; + } + // 2 and under is ethereal visage. + if(iLevel <= i2 && GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, oTarget)) + { + return TRUE; + } + // 1 and under is ghostly visage. + if(iLevel <= i1 && (GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, oTarget) || + GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE))) + { + return TRUE; + } + // False if a level 5+ spell + return FALSE; +} + +// Returns TRUE if any of the checks the oGroupTarget is immune to. +int AI_AOEDeathNecroCheck(object oGroupTarget, int iNecromanticSpell, int iDeathImmune) +{ + if(iNecromanticSpell) + { + if(AI_GetAIHaveSpellsEffect(GlobalHasDeathWardSpell, oGroupTarget)) return TRUE; + } + if(iDeathImmune) + { + return GetIsImmune(oGroupTarget, IMMUNITY_TYPE_DEATH); + } + return FALSE; +} +/*::////////////////////////////////////////////// +//:: Name Get the best HOSTILE spell target +//:: Function Name AI_GetBestAreaSpellTarget +//::////////////////////////////////////////////// + Returns the object to the specifications: + * fRange - Within fRange (fTouchRange 2.25, fShortRange 8.0, fMediumRange 20.0, fLongRange = 40.0) + * fSpread - Radius Size - RADIUS_SIZE_* constants (1.67 to 10.0M) + * nLevel - Used for saving throws/globe checks. Level of the spell added to save checks + * iSaveType = FALSE - SAVING_THROW_FORT/REFLEX/WILL. Not type, but the main save applied with it. + * nShape = SHAPE_SPHERE - SHAPE_* constants. + * nFriendlyFire = FALSE - Can this hurt allies? Best thing is to put + GlobalFriendlyFireHostile - GetIsReactionTypeHostile(oTarget) == TRUE + GlobalFriendlyFireFriendly - GetIsReactionTypeFriendly(oTarget) == FALSE + FALSE - Cannot hurt allies (GetIsEnemy/!GetIsFriend used) + * iDeathImmune = FALSE - Does it use a death save? (!GetIsImmune) + * iNecromanticSpell = FALSE - Is it a necromancy spell? Undead are also checked in this. +//::////////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +object AI_GetBestAreaSpellTarget(float fRange, float fSpread, int nLevel, int iSaveType = FALSE, int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE, int iDeathImmune = FALSE, int iNecromanticSpell = FALSE) +{ + // Before we start, if it can harm us, we don't fire it if there are no + // enemies out of the blast, if it was centered on us + if(nFriendlyFire) + { + if(GlobalRangeToFuthestEnemy <= fSpread) return OBJECT_INVALID; + } + // Delcare objects + // (Target = Loop of nearest creatures. oResturnTarget = Who to return, + // and oGroupTarget is GetFirst/Next in nShape around oTarget) + object oTarget, oReturnTarget, oGroupTarget; + // Delcare Integers + int iCnt, iCntEnemiesOnTarget, iCntAlliesOnTarget, iNoHittingAllies, + iMostOnPerson, iMaxAlliesToHit, iOurToughness; + // Declare Booleans + int bWillHitAlly, bNoHittingAllies, bCheckAlliesHP; + location lTarget; + // Float values + float fDistance, fTargetMustBeFrom; + + // - We don't check specific immunities with cirtain properties/settings + // If we have NOT got the setting and <= 6 int + if(GlobalIntelligence <= i6 && + !GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_IMMUNITY_CHECKING, AI_COMBAT_MASTER)) + { + iDeathImmune = FALSE; + iNecromanticSpell = FALSE; + } + // NOT got extra checks in. + if(!GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SPECIFIC_SPELL_IMMUNITY, AI_COMBAT_MASTER)) + { + nLevel = i5; // This makes the spell think it gets past globes. + // - Problem is that the save DC will increase. Won't affect much. + } + + // We set up the distance that oTarget must be away from us, if it is + // no friendly fire, it will be 0.0 meters, so we always use them. + // - defaults to 0.0 meters unless FF is on + if(nFriendlyFire) + { + fTargetMustBeFrom = fSpread + 0.3; + } + + // Can we hit allies? + bNoHittingAllies = GetSpawnInCondition(AI_FLAG_COMBAT_NEVER_HIT_ALLIES, AI_COMBAT_MASTER); + // Do we check thier HP? (Allies) to see if they survive? + bCheckAlliesHP = GetSpawnInCondition(AI_FLAG_COMBAT_AOE_DONT_MIND_IF_THEY_SURVIVE, AI_COMBAT_MASTER); + + // We will subtract all non-enemies within -8 challenge, upwards. + iOurToughness = GlobalOurHitDice - GetBoundriedAIInteger(AI_AOE_HD_DIFFERENCE, -8, i0, -30); + // The maximum number of allies we can hit... + iMaxAlliesToHit = GetBoundriedAIInteger(AI_AOE_ALLIES_LOWEST_IN_AOE, i3, i1, i90); + // The target to return - set to invalid to start + oReturnTarget = OBJECT_INVALID; + + iCnt = i1; + // - 1.3 Change - made to target only creatures (trying to better performance) + oTarget = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, iCnt); + // Need distance a lot. + fDistance = GetDistanceToObject(oTarget); + // Need to see the target, within nRange around self. + while(GetIsObjectValid(oTarget) && fDistance <= fRange) + { + // Need seen/heard target + if(GetObjectSeen(oTarget) || GetObjectHeard(oTarget)) + { + // Will not fire on self, if it is too near. + if(fDistance > fTargetMustBeFrom) + { + // Reset Targets + // - The person starts the spread at 0, because the object in shape + // will return the target as well. + iCntEnemiesOnTarget = FALSE; + iCntAlliesOnTarget = FALSE; + bWillHitAlly = FALSE; + // Must sue this - needs to use the correct shape + // This only gets creatures in shape to check. + lTarget = GetLocation(oTarget); + oGroupTarget = GetFirstObjectInShape(nShape, fSpread, lTarget); + // If oGroupies is invalid, nothing. Should not - as target will be returned at least. + while(GetIsObjectValid(oGroupTarget) && bWillHitAlly != TRUE) + { + // Sanity check for checking oGroupTarget + if(oGroupTarget != OBJECT_SELF && + !GetIsDead(oGroupTarget) && !GetPlotFlag(oGroupTarget) && + !GetIgnore(oGroupTarget) && !GetIsDM(oGroupTarget) && + !AI_SpellResistanceImmune(oGroupTarget)) + { + // Check necromancy immunity, and death immunity. + // + Save check + // + Level Immunity check + if(!AI_AOEDeathNecroCheck(oGroupTarget, iNecromanticSpell, iDeathImmune) && + !AI_SaveImmuneAOE(oGroupTarget, iSaveType, nLevel) && + !AI_GetSpellLevelEffectAOE(oGroupTarget, nLevel)) + { + if(GetIsEnemy(oGroupTarget)) + { + // Only add one if the person is an enemy, + // and the spell will affect them + iCntEnemiesOnTarget++; + } + // But else if friendly fire, we will subract + // similar non-allies. + else if(nFriendlyFire && + (GetIsFriend(oGroupTarget) || GetFactionEqual(oGroupTarget)) && + GetHitDice(oGroupTarget) >= iOurToughness) + { + if(bNoHittingAllies) + { + bWillHitAlly = TRUE; + } + // We ignore if not got the setting to check HP, + // else, we take one from the iCntEnemiesOnTarget. + else if(bCheckAlliesHP && GetCurrentHitPoints(oGroupTarget) < i50) + { + iCntAlliesOnTarget++; + } + } + } + } + // Get next target in shape + oGroupTarget = GetNextObjectInShape(nShape, fSpread, lTarget); + } + // Make the spell target oTarget if so. + // If it is >= then we set it - it is a futher away target! + if(bWillHitAlly != TRUE && iCntEnemiesOnTarget >= i1 && + iCntAlliesOnTarget < iMaxAlliesToHit && + ((iCntEnemiesOnTarget - iCntAlliesOnTarget) >= iMostOnPerson)) + { + iMostOnPerson = iCntEnemiesOnTarget - iCntAlliesOnTarget; + oReturnTarget = oTarget; + } + } + } + // Gets the next nearest. + iCnt++; + oTarget = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, iCnt); + fDistance = GetDistanceToObject(oTarget); + } + // Will OBJECT_INVALID, or the best target in range. + return oReturnTarget; +} +/*:://///////////////////////////////////////////// +//:: Name: AI_SortSpellImmunities +//:://///////////////////////////////////////////// + Sets the Global Hex for the enemy spell target immunties. + - Uses effects loop and GetIsImmune to comprehend the most. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ +void AI_SortSpellImmunities() +{ + // Error checking + if(!GetIsObjectValid(GlobalSpellTarget)) return; + // Check effects of GlobalSpellTarget + effect eCheck = GetFirstEffect(GlobalSpellTarget); + while(GetIsEffectValid(eCheck)) + { + switch(GetEffectType(eCheck)) + { + case EFFECT_TYPE_TURNED: + case EFFECT_TYPE_FRIGHTENED: + { + AI_SetSpellTargetImmunity(GlobalImmunityFear); + } + break; + case EFFECT_TYPE_STUNNED: + case EFFECT_TYPE_PARALYZE: // Count as stun. Near as dammit + case EFFECT_TYPE_DAZED: // Here for now :-) + { + AI_SetSpellTargetImmunity(GlobalImmunityStun); + } + break; + case EFFECT_TYPE_SLEEP: + { + AI_SetSpellTargetImmunity(GlobalImmunitySleep); + } + break; + case EFFECT_TYPE_POISON: + AI_SetSpellTargetImmunity(GlobalImmunityPoison); + break; + case EFFECT_TYPE_NEGATIVELEVEL: + AI_SetSpellTargetImmunity(GlobalImmunityNegativeLevel); + break; + case EFFECT_TYPE_ENTANGLE: + AI_SetSpellTargetImmunity(GlobalImmunityEntangle); + break; + case EFFECT_TYPE_DOMINATED: + case EFFECT_TYPE_CHARMED: + AI_SetSpellTargetImmunity(GlobalImmunityMind); + break; + case EFFECT_TYPE_CONFUSED: + { + AI_SetSpellTargetImmunity(GlobalImmunityConfusion); + } + break; + case EFFECT_TYPE_DISEASE: + AI_SetSpellTargetImmunity(GlobalImmunityDisease); + break; + case EFFECT_TYPE_CURSE: + AI_SetSpellTargetImmunity(GlobalImmunityCurse); + break; + case EFFECT_TYPE_BLINDNESS: + case EFFECT_TYPE_DEAF: + AI_SetSpellTargetImmunity(GlobalImmunityBlindDeaf); + break; + case EFFECT_TYPE_SLOW: + AI_SetSpellTargetImmunity(GlobalImmunitySlow); + break; + // All + case EFFECT_TYPE_PETRIFY: + AI_SetSpellTargetImmunity(GlobalImmunityPetrify); + break; + // Defualt, check spell ID + default: + { + switch(GetEffectSpellId(eCheck)) + { + // Mantals + case SPELL_GREATER_SPELL_MANTLE: + case SPELL_SPELL_MANTLE: + case SPELL_LESSER_SPELL_MANTLE: + AI_SetSpellTargetImmunity(GlobalImmunityMantalProtection); + break; + // If the target has death ward, they are immune. + case SPELL_DEATH_WARD: + AI_SetSpellTargetImmunity(GlobalImmunityDeath); + break; + // Spells that stop negative energy spells: Shadow Shield, Negative energy protection + case SPELL_SHADOW_SHIELD: + case SPELL_UNDEATHS_ETERNAL_FOE:// New one SoU + { + AI_SetSpellTargetImmunity(GlobalImmunityNegativeEnergy); + AI_SetSpellTargetImmunity(GlobalImmunityNecromancy); + } + break; + case SPELL_NEGATIVE_ENERGY_PROTECTION: + AI_SetSpellTargetImmunity(GlobalImmunityNegativeEnergy); + break; + case SPELL_CLARITY: + case SPELL_MIND_BLANK: + case SPELL_LESSER_MIND_BLANK:// New one SoU + AI_SetSpellTargetImmunity(GlobalImmunityMind); + break; + case SPELL_UNHOLY_AURA: + case SPELL_MAGIC_CIRCLE_AGAINST_GOOD: + case SPELL_PROTECTION_FROM_GOOD:// New one SoU + if(GlobalOurGoodEvil == ALIGNMENT_GOOD) AI_SetSpellTargetImmunity(GlobalImmunityMind); + break; + case SPELL_HOLY_AURA: + case SPELL_PROTECTION_FROM_EVIL: + case SPELL_MAGIC_CIRCLE_AGAINST_EVIL:// New one SoU + if(GlobalOurGoodEvil == ALIGNMENT_EVIL) AI_SetSpellTargetImmunity(GlobalImmunityMind); + break; + } + } + break; + } + eCheck = GetNextEffect(GlobalSpellTarget); + } + // Immunity Things + // We only use these if 7+ Intelligence + if(GlobalIntelligence >= i7 || + GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_IMMUNITY_CHECKING, AI_COMBAT_MASTER)) + { + // Death immunity + if(!AI_GetSpellTargetImmunity(GlobalImmunityDeath) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DEATH)) + AI_SetSpellTargetImmunity(GlobalImmunityDeath); + // confusion immunity + if(!AI_GetSpellTargetImmunity(GlobalImmunityConfusion) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_CONFUSED)) + AI_SetSpellTargetImmunity(GlobalImmunityConfusion); + // Negative level immunity + if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_NEGATIVE_LEVEL)) + AI_SetSpellTargetImmunity(GlobalImmunityNegativeLevel); + // Mind immunity + if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_MIND_SPELLS)) + AI_SetSpellTargetImmunity(GlobalImmunityMind); + // Old ones, I doubt needed with GetIsImmune in. + // - Dragon race, Construct, Undead and Empty Body. + // Fear checking + if(!AI_GetSpellTargetImmunity(GlobalImmunityFear) && + (AI_GetSpellTargetImmunity(GlobalImmunityMind) || + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_FEAR))) + { + AI_SetSpellTargetImmunity(GlobalImmunityFear); + } + // Old ones for fear. + // - Undead, Dragon, Construct, Outsider + // - Feat Aura of courage, resist natures lure + // This stops curses. + if(!AI_GetSpellTargetImmunity(GlobalImmunityCurse) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_CURSED)) + AI_SetSpellTargetImmunity(GlobalImmunityCurse); + // This stops poison. + if(!AI_GetSpellTargetImmunity(GlobalImmunityPoison) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_POISON)) + AI_SetSpellTargetImmunity(GlobalImmunityPoison); + // This will stop blindness/deafness spells + if(!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) && + (GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_BLINDNESS) || + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DEAFNESS))) + AI_SetSpellTargetImmunity(GlobalImmunityBlindDeaf); + // Stun (And paralsis) + if(!AI_GetSpellTargetImmunity(GlobalImmunityStun) && + (GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_STUN) || + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_PARALYSIS))) + AI_SetSpellTargetImmunity(GlobalImmunityStun); + // This stops entanglement. + if(!AI_GetSpellTargetImmunity(GlobalImmunityEntangle) && + (GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_ENTANGLE) || + GetHasFeat(FEAT_WOODLAND_STRIDE, GlobalSpellTarget))) + AI_SetSpellTargetImmunity(GlobalImmunityEntangle); + // Sleep + if(!AI_GetSpellTargetImmunity(GlobalImmunitySleep) && + (GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_SLEEP) || + GetHasFeat(FEAT_IMMUNITY_TO_SLEEP, GlobalSpellTarget))) + AI_SetSpellTargetImmunity(GlobalImmunitySleep); + // Poison + if(!AI_GetSpellTargetImmunity(GlobalImmunityPoison) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_POISON)) + AI_SetSpellTargetImmunity(GlobalImmunityPoison); + // Disease + if(!AI_GetSpellTargetImmunity(GlobalImmunityDisease) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DISEASE)) + AI_SetSpellTargetImmunity(GlobalImmunityDisease); + // We don't check these feats for immune disease/poison till checked + // if the immune doesn't include htem + // GetHasFeat(FEAT_RESIST_DISEASE, GlobalSpellTarget) + // GetHasFeat(FEAT_VENOM_IMMUNITY, GlobalSpellTarget) + // GetHasFeat(FEAT_DIAMOND_BODY, GlobalSpellTarget) + if(!AI_GetSpellTargetImmunity(GlobalImmunitySlow) && + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_SLOW)) + AI_SetSpellTargetImmunity(GlobalImmunitySlow); + // Domination (+Charm) + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination)) + { + if(GetIsObjectValid(GetMaster(GlobalSpellTarget)) || + GlobalSpellTargetRace == RACIAL_TYPE_ELEMENTAL || + GlobalSpellTargetRace == RACIAL_TYPE_UNDEAD || + GlobalSpellTargetRace == RACIAL_TYPE_VERMIN || + GlobalSpellTargetRace == RACIAL_TYPE_CONSTRUCT || + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DOMINATE) || + GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_CHARM) || + GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_DOMINATED))) + { + AI_SetSpellTargetImmunity(GlobalImmunityDomination); + } + } + } + int iApperance = GetAppearanceType(GlobalSpellTarget); + // We won't use petrify on those immune, though. This is taken from + // x0_i0_spells. Stops silly basalisks or something. :-) + // - ANY intelligence + if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify)) + { + switch(GetAppearanceType(GlobalSpellTarget)) + { + case APPEARANCE_TYPE_BASILISK: + case APPEARANCE_TYPE_COCKATRICE: + case APPEARANCE_TYPE_MEDUSA: + case APPEARANCE_TYPE_ALLIP: + case APPEARANCE_TYPE_ELEMENTAL_AIR: + case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_EARTH: + case APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_FIRE: + case APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_WATER: + case APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER: + case APPEARANCE_TYPE_GOLEM_STONE: + case APPEARANCE_TYPE_GOLEM_IRON: + case APPEARANCE_TYPE_GOLEM_CLAY: + case APPEARANCE_TYPE_GOLEM_BONE: + case APPEARANCE_TYPE_GORGON: + case APPEARANCE_TYPE_HEURODIS_LICH: + case APPEARANCE_TYPE_LANTERN_ARCHON: + case APPEARANCE_TYPE_SHADOW: + case APPEARANCE_TYPE_SHADOW_FIEND: + case APPEARANCE_TYPE_SHIELD_GUARDIAN: + case APPEARANCE_TYPE_SKELETAL_DEVOURER: + case APPEARANCE_TYPE_SKELETON_CHIEFTAIN: + case APPEARANCE_TYPE_SKELETON_COMMON: + case APPEARANCE_TYPE_SKELETON_MAGE: + case APPEARANCE_TYPE_SKELETON_PRIEST: + case APPEARANCE_TYPE_SKELETON_WARRIOR: + case APPEARANCE_TYPE_SKELETON_WARRIOR_1: + case APPEARANCE_TYPE_SPECTRE: + case APPEARANCE_TYPE_WILL_O_WISP: + case APPEARANCE_TYPE_WRAITH: + case APPEARANCE_TYPE_BAT_HORROR: + AI_SetSpellTargetImmunity(GlobalImmunityPetrify); + break; + } + } + // Earthquake check removed. +/* Still got: + IMMUNITY_TYPE_ABILITY_DECREASE IMMUNITY_TYPE_AC_DECREASE + IMMUNITY_TYPE_ATTACK_DECREASE IMMUNITY_TYPE_CRITICAL_HIT + IMMUNITY_TYPE_DAMAGE_DECREASE IMMUNITY_TYPE_DAMAGE_IMMUNITY_DECREASE + IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE IMMUNITY_TYPE_SAVING_THROW_DECREASE + IMMUNITY_TYPE_SILENCE IMMUNITY_TYPE_SKILL_DECREASE + IMMUNITY_TYPE_SPELL_RESISTANCE_DECREASE IMMUNITY_TYPE_TRAP */ + +} + +/*:://///////////////////////////////////////////// +//:: Name: AI_ActionDispelAOE +//:://///////////////////////////////////////////// + Dispels or moves out of the oAOE. + - If we have >= iMax AND <= iDamage HP... + AND under iPercent total HP... + - We Dispel, or move out of the AOE. Move if friendly. + Note: We get nearest AOE of sAOE tag. Must be valid as well. + + 1.3 - Added +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ +int AI_ActionDispelAOE(int iSpell, int iDamageOnly, float fRange, int iDamage, int iMax, int iPercent) +{ + object oAOE = GetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(iSpell)); + // Get damage done + int iLastDamage = GetAIInteger(ObjectToString(oAOE)); + // Delete/clean up + DeleteAIInteger(ObjectToString(oAOE)); + // Check we will be affected, or are affected, by the AOE at all! + if(GetIsObjectValid(oAOE) && GetDistanceToObject(oAOE) <= fRange && + // + Damaged from that AOE, or affected by that AOE. + (iLastDamage >= TRUE || (GetHasSpellEffect(iSpell) && !iDamageOnly))) + { + // Either Under iMax AND under + if((GlobalOurMaxHP >= iMax && GlobalOurCurrentHP <= iDamage) || + GlobalOurPercentHP <= iPercent || !GlobalAnyValidTargetObject) + { + // 16: "[AOE Call] Moving out of/Dispeling an AOE. [Tag] " + GetTag(oAOE) + DebugActionSpeakByInt(16, oAOE); + // If an enemy, Dispel it. + if(!GetIsFriend(GetAreaOfEffectCreator(oAOE))) + { + // 11 = Harmful AOE indis. 2 = Harmful ranged. + // Gust of Wind. Level 3 (Bard/Mage) Dispels all AOE's in radius, and save VS fort for 3 round knockdown. + if(AI_ActionCastSpell(SPELL_GUST_OF_WIND, SpellHostAreaInd, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE; + // Worst to best, then move...wind gust best! + // - Note that they have a % chance to remove stuff... + if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE; + if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, oAOE, i13, TRUE, ItemHostRanged)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, oAOE, i16, TRUE, ItemHostRanged)) return TRUE; + if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, oAOE, i19, TRUE, ItemHostAreaInd)) return TRUE; + } + // Else move. We force this until we are out of the AOE + SetAIObject(AI_AOE_FLEE_FROM, oAOE); + SetLocalFloat(OBJECT_SELF, AI_AOE_FLEE_FROM_RANGE, fRange + GlobalOurReach); + SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_OUT_OF_AOE); + ActionMoveAwayFromLocation(GetLocation(oAOE), TRUE, fRange + GlobalOurReach); + return TRUE; + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: Special Checks +//:://///////////////////////////////////////////// + This will check for darkness, AOE spells, + time stop stored, and a few other things. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptSpecialChecks() +{ + // Delete stored ints if not in time stop. + if(!GlobalInTimeStop) AI_DeleteTimeStopStored(); + + // - Fleeing is performed in ClearAllAction()'s wrapper + + // Darkness! the ENEMY OF THE ARTIFICAL INTELlIGENCE!!! MUAHAHAH! WE COMBAT IT WELL! (sorta) + if((AI_GetAIHaveSpellsEffect(GlobalEffectDarkness) && + !AI_GetAIHaveEffect(GlobalEffectUltravision) && + !AI_GetAIHaveEffect(GlobalEffectTrueSeeing)) + // Check nearest enemy with darkness effect. Use heard only (they won't be seen!) + || GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_DARKVISION, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD))) + { + // Ultravision - Talent category 10 (Benificial enchancement Self). + if(AI_ActionCastSpell(SPELL_DARKVISION, SpellEnhSelf, OBJECT_SELF, i13, FALSE, ItemEnhSelf, PotionEnh)) return TRUE; + // 1.30/SoU trueeing should Dispel it. (Benificial conditional AOE) + if(AI_ActionCastSpell(SPELL_TRUE_SEEING, SpellConAre, OBJECT_SELF, i16, FALSE, ItemConAre, PotionCon)) return TRUE; + + // If we are a rubbish fightingclass OR we cannot hear any enemy to attack...move or something. + if(!GlobalValidNearestSeenEnemy && + // If only someone else has it, don't worry, its only if we cannot see anyone + (GlobalOurChosenClass == CLASS_TYPE_WIZARD || + GlobalOurChosenClass == CLASS_TYPE_SORCERER || + GlobalOurChosenClass == CLASS_TYPE_FEY)) + { + // 17: "[DCR:Special] Darkness + Caster. No seen enemy. Dispel/Move." + DebugActionSpeakByInt(17); + // Dispel it - trying nearest creature, if has darkness as well + // 11 = Harmful AOE indis. 2 = Harmful ranged. + // WORST to BEST as all of them never check caster levels for AOEs (Silly bioware!) + if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, OBJECT_SELF, i12, TRUE, ItemHostAreaInd)) return TRUE; + if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, OBJECT_SELF, i13, TRUE, ItemHostRanged)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, OBJECT_SELF, i16, TRUE, ItemHostRanged)) return TRUE; + if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, OBJECT_SELF, i19, TRUE, ItemHostAreaInd)) return TRUE; + + ActionMoveAwayFromLocation(GetLocation(OBJECT_SELF), TRUE, f6); + return TRUE; + } + } + object oInvisible = GetAIObject(AI_LAST_TO_GO_INVISIBLE); + DeleteAIObject(AI_LAST_TO_GO_INVISIBLE); + // Intelligence: 8+ means HIPS check. There is a 80% chance. + if((!GetIsObjectValid(oInvisible) || GetObjectSeen(oInvisible)) && + GlobalIntelligence >= i8 && d10() <= i8) + { + // Makes the AI cast seeing spells against shadowdancers who will HIPS + oInvisible = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_SHADOWDANCER, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_IS_ALIVE, TRUE); + DeleteLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE); + } + // If we have a previously set invisible enemy or if high intelligence, a shadowdancer in range. + if(GetIsObjectValid(oInvisible)) + { + // 1.30/SoU trueeing should Dispel it. (Benificial conditional AOE) + if(AI_ActionCastSpell(SPELL_TRUE_SEEING, SpellConAre, OBJECT_SELF, i16, FALSE, ItemConAre, PotionCon)) return TRUE; + // Invisiblity purge! SpellConAre + if(AI_ActionCastSpell(SPELL_INVISIBILITY_PURGE, SpellConAre, OBJECT_SELF, i14, FALSE, ItemConAre, PotionCon)) return TRUE; + // This should work well as well! SpellConSinTar + if(AI_ActionCastSpell(SPELL_SEE_INVISIBILITY, SpellConSinTar, OBJECT_SELF, i12, FALSE, ItemConSinTar, PotionCon)) return TRUE; + + location lTarget = GetLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE); + DeleteLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE); + + // Check location validness + if(GetIsObjectValid(GetAreaFromLocation(lTarget))) + { + // We will cast at thier previous location, using a temp object we actually + // create there for a few seconds. + oInvisible = CreateObject(OBJECT_TYPE_PLACEABLE, "", GetLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE)); + // Check range + if(GetIsObjectValid(oInvisible) && + GetDistanceToObject(oInvisible) < f40) + { + // Best to worst + if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, oInvisible, i19, TRUE, ItemHostAreaInd)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, oInvisible, i16, TRUE, ItemHostRanged)) return TRUE; + if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, oInvisible, i13, TRUE, ItemHostRanged)) return TRUE; + if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, oInvisible, i12, TRUE, ItemHostAreaInd)) return TRUE; + // Destroy the placeable after we have got the location ETC. + DestroyObject(oInvisible); + } + } + } + // AOE spells + // - Either we Dispel it or move out of it. + // - Dispeling more if higher INT. + // - We always move if it does great damage + // - Low intelligence ignores this (probably to thier doom). + + // We have already: + // - Set if there is an AOE that affects us cast at us. + // - Checked any saves avalible, and immunities (Intelligence based) + + // Delcarations. + object oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT); + float fDistance = GetDistanceToObject(oAOE); + + // First check: We randomly check this if INT 1-5, and always check 6+ + if(GlobalIntelligence >= (i6 - d6()) && + // Second: Need a valid AOE, of course! + GetIsObjectValid(oAOE) && + // Third: Needs to be in 10.0 M + fDistance <= RADIUS_SIZE_COLOSSAL) + { + int iElemental = AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections); + int iPhisical = AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections); + // We can check the timers added to spells, to check for AOEs we are in. + + // Long range ones...already made sure of collosal range! + // Storm of vengance...a lot of damage (so we need only 30HP left) + // Stats: Raduis 10! d6(3) , Get Is Friend/Friendly. + // Either: Electical (reflex) and stun (failed save), else Acid, d6(6). + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_STORM_OF_VENGEANCE)) && !iElemental) + { + if(AI_ActionDispelAOE(SPELL_STORM_OF_VENGEANCE, TRUE, RADIUS_SIZE_COLOSSAL, i30, i50, i20)) return TRUE; + } + // Next range...6.7 + if(fDistance <= RADIUS_SIZE_HUGE) + { + // Creeping doom! + // This can get a lot of damage. We might check the counter status... + // Stats: Radius 6.7. Initial d20 damage (can't stop), Slow as well. Reaction Type Friendly. + // d6(Amount of rounds in effect) each round after that, HURTS! (up to 1000) + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_CREEPING_DOOM)) && !iPhisical) + { + if(AI_ActionDispelAOE(SPELL_CREEPING_DOOM, TRUE, RADIUS_SIZE_HUGE, GetLocalInt(oAOE, "NW_SPELL_CONSTANT_CREEPING_DOOM1" + ObjectToString(GetAreaOfEffectCreator(oAOE))) * i6, i40, i40)) return TRUE; + } + // Stinking cloud! + // No damage, only daze. If we are immune to it - fine. + // Note: PATCH 1.30 should make daze walkable - we can wait + // to be affected then walk out of the area :-) + // Only fortitude save. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_STINKING_CLOUD))) + { + if(AI_ActionDispelAOE(SPELL_STINKING_CLOUD, TRUE, RADIUS_SIZE_HUGE, i25, i60, i35)) return TRUE; + } + // Grease + // Stats: Radius 6. (count as 6.7) We get slowed (unless Woodland Stride) in it always. + // On a reflex save each round, knockdown effect (4 seconds). Reaction Type Friendly. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_GREASE))) + { + if(AI_ActionDispelAOE(SPELL_GREASE, FALSE, RADIUS_SIZE_HUGE, i10, i25, i15)) return TRUE; + } + // Wall of fire... + // Stats: 10M x 2M rectangle. d6(4) Fire damage (Reflex save). + // Notes: level 5/6 spell. Not bad, up to 24 damage! Reaction Type Frendly. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_WALL_OF_FIRE)) && !iElemental) + { + if(AI_ActionDispelAOE(SPELL_WALL_OF_FIRE, TRUE, RADIUS_SIZE_HUGE, i20, i50, i25)) return TRUE; + } + // Blade Barrier (special shape!) + // Stats: 10M x 1M rectangle. d6(CasterLevel) Piercing damage (Reflex save). + // Notes: LOTs of damage! warning about this one! Reaction Type friendly. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_BLADE_BARRIER)) && !iPhisical) + { + if(AI_ActionDispelAOE(SPELL_BLADE_BARRIER, TRUE, RADIUS_SIZE_HUGE, i30, i50, i35)) return TRUE; + } + // Next range...5.0 + if(fDistance <= RADIUS_SIZE_LARGE) + { + // Acid Fog spell + // Stats: Radius 5. Damage: d6(2), Fort/2. Slow on failed Fort save (On Enter) + // Notes: Level 6 spell. Uses ReactionType, not GetFriend. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_ACID_FOG))) + { + // Ignore slowing effect + if(AI_ActionDispelAOE(SPELL_ACID_FOG, TRUE, RADIUS_SIZE_HUGE, i20, i40, i25)) return TRUE; + } + // Inceniary Cloud spell + // Stats: Radius 5. Damage: d6(4), Reflex. Slow always. + // Notes: Level 8 spell. Uses ReactionTypeFriendly, not GetFriend. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_INCENDIARY_CLOUD)) && !iElemental) + { + // Ignore slow + if(AI_ActionDispelAOE(SPELL_INCENDIARY_CLOUD, TRUE, RADIUS_SIZE_HUGE, i30, i50, i35)) return TRUE; + } + // Cloudkill spell + // Stats: Radius 5. Under 3HD Die. 4-7 Save VS Death (Fort) + // All Damage d10(), no save. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_CLOUDKILL))) + { + // Damage only + if(AI_ActionDispelAOE(SPELL_CLOUDKILL, TRUE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE; + } + // Evards Black Tenticals. + // Stats: Radius 5. 1d6 damage if d20 + 5 hits the target. (Reaction Type Hostile) + // May paralize them as well, on a fort save. Up to 5 of these tenticals BTW. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_EVARDS_BLACK_TENTACLES)) && !iPhisical) + { + if(AI_ActionDispelAOE(SPELL_EVARDS_BLACK_TENTACLES, TRUE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE; + } + // Mind Fog spell + // Stats: Radius 5. + // -10 to will saves, if fail a will save (and it continues out of it). + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_MIND_FOG)) && + GetLocalInt(OBJECT_SELF, AI_ABILITY_DECREASE) > FALSE) + { + // No damage + if(AI_ActionDispelAOE(SPELL_MIND_FOG, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE; + } + // Entangle + // Stats: Radius 5. Reaction Type hostile used. + // Entangle effect against a reflex save each round (for 12 seconds). + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_ENTANGLE)) && + AI_GetAIHaveEffect(GlobalEffectEntangle)) + { + // No damage + if(AI_ActionDispelAOE(SPELL_ENTANGLE, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE; + } + // Vine mine + // Stats: Radius as Entangle - Radius 5 + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_VINE_MINE_ENTANGLE)) && + AI_GetAIHaveEffect(GlobalEffectEntangle)) + { + // " " + if(AI_ActionDispelAOE(SPELL_VINE_MINE_ENTANGLE, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE; + } + // Hamper movement one + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_VINE_MINE_HAMPER_MOVEMENT)) && + AI_GetAIHaveEffect(GlobalEffectMovementSpeedDecrease)) + { + if(AI_ActionDispelAOE(SPELL_VINE_MINE_HAMPER_MOVEMENT, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE; + } + } + // Web + // Stats: 6.67 Radius + // Notes: Just a large entange + Slow for anyone. + if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_WEB)) && + AI_GetAIHaveEffect(GlobalEffectEntangle)) + { + if(AI_ActionDispelAOE(SPELL_WEB, FALSE, RADIUS_SIZE_HUGE, i30, i50, i35)) return TRUE; + } + } + } + // End AOE + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: ReturnHealingInfo +//:://///////////////////////////////////////////// + This will do 1 of two things, with the spell ID + 1. If iHealAmount is FALSE, it will return what number (rank) in order, which is also used for level checking + 2. If TRUE, it will return the average damage it will heal. +//:://///////////////////////////////////////////// +//:: Created by: Jasperre +//:://///////////////////////////////////////////*/ +int AI_ReturnHealingInfo(int iSpellID, int iSelf = FALSE, int iHealAmount = FALSE) +{ + // On error - return 0 rank, or 0 heal. + if(iSpellID <= FALSE) return FALSE; + switch(iSpellID) + { + // RANK - NAME = D8 AMOUNTs + RANGE OF CLERIC LEVELS ADDED. MAX. + // AVERAGE OF DICE. ABOUT 2/3 OF MODIFIERS. + case SPELL_CURE_CRITICAL_WOUNDS: + { + // 20 - Critical = 4d8 + 7-20. Max of 32. Take as 24. Take modifiers as 10. + if(iHealAmount){ return i35; } else { return i10; } + } + break; + case SPELLABILITY_LESSER_BODY_ADJUSTMENT: + case SPELL_CURE_LIGHT_WOUNDS: + { + // 10 - Lesser Bodily Adjustment = 1d8 + 1-5. Max of 8. Take as 6. Take modifiers as 3. + // NOTE: same spell script as Cure Light Wounds, but no concentration check! + // 8 - Light = 1d8 + 2-5. Max of 8. Take as 6. Take modifiers as 3. + if(iHealAmount){ return i9; } else { return i2; } + } + break; + case SPELL_CURE_MINOR_WOUNDS: + { + // 4 - Minor = 1. Max of 1. Take as 1. Take modifiers as 0. + // No need for check - healing and rank are both 1. + return i1; + } + break; + case SPELL_CURE_MODERATE_WOUNDS: + { + // 12 - Moderate = 2d8 + 3-10. Max of 16. Take as 12. Take modifiers as 5. + if(iHealAmount){ return i17; } else { return i3; } + } + break; + case SPELL_CURE_SERIOUS_WOUNDS: + { + // 16 - Serious = 3d8 + 5-15. Max of 24. Take as 18. Take modifiers as 7. + if(iHealAmount){ return i25; } else { return i4; } + } + break; + case SPELL_HEALING_CIRCLE: + { + // 14 - Healing circle = 1d8 + 9-20. Max of 8. Take as 8. Take modifiers as 10. + if(iHealAmount){ return i18; } else { return i4; } + } + break; + case AI_SPELLABILITY_CURE_CRITICAL_WOUNDS_OTHER: + { + // As cure critical wounds, but cures another and damages us... + // No idea.. :-) + if(iSelf == TRUE) + { + // 20 - Critical = 4d8 + 7-20. Max of 32. Take as 24. Take modifiers as 9. + if(iHealAmount){ return i35; } else { return i9; } + } + } + break; + } + // On error - return 0 rank, or 0 heal. + return FALSE; +} +// Wrappers action Use Talent with debug string +void AI_ActionUseTalentDebug(talent tChosenTalent, object oTarget) +{ + // 18: "[DRC:Talent] Using Talent (Healing). [TalentID] " + IntToString(GetIdFromTalent(tChosenTalent)) + " [Target] " + GetName(oTarget) + DebugActionSpeakByInt(18, oTarget, GetIdFromTalent(tChosenTalent)); + ActionUseTalentOnObject(tChosenTalent, oTarget); +} +/*:://///////////////////////////////////////////// +//:: Name: AI_ActionHealObject +//:://///////////////////////////////////////////// + This will heal oTarget using the best spell it can, even ones it can + spontaeously cast. + - Just does best spell. + - May use spontaneous ones as well. :-) + - Called from AI_AttemptHealing_Self and AI_AttemptHealing_Ally +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_ActionHealObject(object oTarget) +{ + // Set healing talents + talent tAOEHealing, tTouchHealing, tPotionHealing; + // These are left at 0 if we have no SpawnInCondition setting them. + int iAOEHealingValue, iTouchHealingValue, iPotionValue; + + // We set up OnSpawn if any of the 3 talents are valid + if(GetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_HEALING_AREAEFFECT, AI_VALID_SPELLS)) + { + tAOEHealing = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_AREAEFFECT, i20); + iAOEHealingValue = GetIdFromTalent(tAOEHealing); + } + if(GetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_HEALING_TOUCH, AI_VALID_SPELLS)) + { + tTouchHealing = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH, i20); + iTouchHealingValue = GetIdFromTalent(tTouchHealing); + } + // First, we check for heal spells in the talents we have. They are special cases! + // We set this to the right value if the target is us. + int iTargetCurrentHP = GlobalOurCurrentHP; + int iTargetMaxHP = GlobalOurMaxHP; + int iTargetPercentHP = GlobalOurPercentHP; + int iTargetSelf = TRUE; + // Check...might still be minus one, anyway. + if(oTarget == OBJECT_SELF) + { + tPotionHealing = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_POTION, i20); + iPotionValue = GetIdFromTalent(tPotionHealing); + } + else // If not us, we don't know HP values. + { + iTargetCurrentHP = GetCurrentHitPoints(oTarget); + iTargetMaxHP = GetMaxHitPoints(oTarget); + // Percent = Current/Max * 100 + iTargetPercentHP = AI_GetPercentOf(iTargetCurrentHP, iTargetMaxHP); + iTargetSelf = FALSE; + } + + // Check for heal amounts, oTarget might have to be X damaged. + // - If we have someone using knockdown on us, we make SURE we cast it sooner + if(iTargetCurrentHP < i25 || iTargetPercentHP < i30 || + // High damage at once for target (over 40) + (GetLocalInt(oTarget, AI_INTEGER + AI_HIGHEST_DAMAGE_AMOUNT) > i40 && iTargetPercentHP < i40) || + // Had knockdown used on us lately + (GetLocalTimer(AI_TIMER_KNOCKDOWN) && + (iTargetCurrentHP < i40 || iTargetPercentHP < i40))) + { + if(iPotionValue == SPELL_HEAL) + { + AI_EquipBestShield(); + // Potion spell + AI_ActionUseTalentDebug(tPotionHealing, OBJECT_SELF); + return TRUE; + } + else if(iTouchHealingValue == SPELL_HEAL) + { + AI_EquipBestShield(); + // Touch heal spell + AI_ActionUseTalentDebug(tTouchHealing, oTarget); + return TRUE; + } + else if(iAOEHealingValue == SPELL_MASS_HEAL) + { + AI_EquipBestShield(); + // Mass heal spell + AI_ActionUseTalentDebug(tAOEHealing, oTarget); + return TRUE; + } + } + // Else, another talent - IE critical wounds and under. + + // Are any of them valid? + if(GlobalBestSpontaeousHealingSpell >= i1 || + iAOEHealingValue >= i1 || iTouchHealingValue >= i1 || iPotionValue >= i1) + { + // We must work out the rank of each thing we can use, IE 10 best, + // 1 worst or whatever :-D + int iSpontaeousRank, iPotionRank, iAOERank, iTouchRank, iRank, + iDamageNeededToBeDone, iRankOfSpell, // iRank must be 0, which it starts at. + // Spontaeous or a talent? 1 = Talent, 2 = Spontaeous, 0 = Error/none + iTalentOrSpon; + // What talent should we use? + talent tToUse; + // We check if we have a spon. spell, and set its rank. Same with others. + iSpontaeousRank = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf); + iAOERank = AI_ReturnHealingInfo(iAOEHealingValue, iTargetSelf); + iTouchRank = AI_ReturnHealingInfo(iTouchHealingValue, iTargetSelf); + iPotionRank = AI_ReturnHealingInfo(iPotionValue, iTargetSelf); + + // Need a valid rank - we COULD get healing feats with these talents + if(iAOERank >= i1 || iAOERank >= i1 || iPotionRank >= i1 || iSpontaeousRank >= i1) + { + // Determine what to use... + // Potion VS area. + if(iPotionRank >= iAOERank && iPotionRank >= i1)// Make sure we have a potion rank + { + // Potion VS touch and so on. + if(iPotionRank >= iTouchRank) + { + if(iPotionRank >= iSpontaeousRank)// IE beats all 3 + { + iTalentOrSpon = i1; + tToUse = tPotionHealing; + iRank = iPotionRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(iPotionValue, iTargetSelf, TRUE); + } + else if(iSpontaeousRank >= i1) + { + iTalentOrSpon = i2; + iRank = iSpontaeousRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE); + } + } + else if(iTouchRank >= iSpontaeousRank && iTouchRank >= i1)// Make sure we have a touch rank + { + iTalentOrSpon = i1; + tToUse = tTouchHealing; + iRank = iTouchRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(iTouchHealingValue, iTargetSelf, TRUE); + } + else if(iSpontaeousRank >= i1) + { + iTalentOrSpon = i2; + iRank = iSpontaeousRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE); + } + } + // Area VS Touch + else if(iAOERank >= iTouchRank && iAOERank >= i1)// Make sure we have an AOE rank + { + if(iAOERank >= iSpontaeousRank) + { + iTalentOrSpon = i1; + tToUse = tAOEHealing; + iRank = iPotionRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(iAOEHealingValue, iTargetSelf, TRUE); + } + else if(GlobalBestSpontaeousHealingSpell) + { + iTalentOrSpon = i2; + iRank = iSpontaeousRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE); + } + } + // Touch VS Spontaeous + else if(iTouchRank >= iSpontaeousRank && iTouchRank >= i1) + { + iTalentOrSpon = i1; + tToUse = tTouchHealing; + iRank = iTouchRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(iTouchHealingValue, iTargetSelf, TRUE); + } + // It should be Spontaeous at the very end, BUT, we need to make sure its valid + else if(iSpontaeousRank >= i1) + { + iTalentOrSpon = i2; + iRank = iSpontaeousRank; + iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE); + } + // Now, have we a talent or spell to use? + if(iTalentOrSpon && iRank >= i1) + { + // If the current HP is under the damage that is healed, + // or under 25% left :-) + if(iTargetCurrentHP <= (iTargetMaxHP - iDamageNeededToBeDone) || + (iTargetPercentHP < i25)) + { + // Level check. Our HD must be a suitble amount, or no melee attackers. + // Even if we are healing others - we don't bother with bad healing + + // Rank of the spell, can be in a range over our HD -5. + if(((iRank - GlobalOurHitDice) >= -i5) || + // OR Rank of 16+ (critical wounds or more) are always done. + (iRank >= i16) || + // OR no valid nearest enemy. + (!GlobalAnyValidTargetObject) || + // OR 0 Melee attackers, and a larger range (up to -10 our HD) + ((GlobalMeleeAttackers < i1) && ((iRank - GlobalOurHitDice) >= -10))) + { + // Use it...and attack with a bow. Always return TRUE. + // Need only one debug :-P + // 19: "[DCR:Healing] (Should) Healing [Target]" + GetName(oTarget) + " [CurrentHP|Max|ID|Rank|Power] " + IntToString((10000 * iTargetCurrentHP) + (1000 * iTargetMaxHP) + (100 * GetIdFromTalent(tToUse)) + (10 * iRank) + (iDamageNeededToBeDone)) + DebugActionSpeakByInt(19, oTarget, ((10000 * iTargetCurrentHP) + (1000 * iTargetMaxHP) + (100 * GetIdFromTalent(tToUse)) + (10 * iRank) + (iDamageNeededToBeDone))); + + // Talent! + if(iTalentOrSpon == i1) + { + if(GetIsTalentValid(tToUse)) + { + AI_EquipBestShield(); + // No AI_ActionUseTalentDebug, just normal. Debug string above. + DeleteLocalInt(OBJECT_SELF, AI_SPONTAEUOUSLY_CAST_HEALING); + ActionUseTalentOnObject(tToUse, oTarget); + return TRUE; + } + } + // Spontaeous Spell! + else //if(iTalentOrSpon == i2) // (is always 2 or 1 or 0) + { + // Use the same thing as the inflict spell + if(AI_ActionCastSpontaeousSpell(GlobalBestSpontaeousHealingSpell, TRUE, oTarget)) return TRUE; + } + } + } + } + } + // Else, no healing is valid...ranks, ETC. + else + // Check if it is us and so on. + // If it is us, and we have no potion spell, + if(oTarget == OBJECT_SELF && + GlobalOurPercentHP < i40 && !iPotionRank && + GetSpawnInCondition(AI_FLAG_OTHER_CHEAT_MORE_POTIONS, AI_OTHER_MASTER)) + { + if(!GetIsObjectValid(GetItemPossessedBy(OBJECT_SELF, "nw_it_mpotion003"))) + { + // 20: "[DCR Healing] Boss Action, create Critical Wounds potion" + DebugActionSpeakByInt(20); + CreateItemOnObject("nw_it_mpotion003"); + } + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: AI_ActionHealUndeadObject +//:://///////////////////////////////////////////// + This will heal oTarget using the best negative energy spell it can use, + - Used best spell (including harm, ETC) +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ +int AI_ActionHealUndeadObject(object oTarget) +{ + // First, we check for heal spells in the talents we have. They are special cases! + // We set this to the right value if the target is us. + int iTargetCurrentHP = GlobalOurCurrentHP; + int iTargetMaxHP = GlobalOurMaxHP; + int iTargetPercentHP = GlobalOurPercentHP; + // If not us, we don't know HP values. + if(oTarget != OBJECT_SELF) + { + iTargetCurrentHP = GetCurrentHitPoints(oTarget); + iTargetMaxHP = GetMaxHitPoints(oTarget); + // Percent = Current/Max * 100 + iTargetPercentHP = AI_GetPercentOf(iTargetCurrentHP, iTargetMaxHP); + } + // Check for Harm amounts, oTarget might have to be X damaged. + if(iTargetCurrentHP < i25 || iTargetPercentHP < i30 || + // High damage at once for target (over 40) + (GetLocalInt(oTarget, AI_INTEGER + AI_HIGHEST_DAMAGE_AMOUNT) > i40 && iTargetPercentHP < i40) || + // Had knockdown used on us lately + (GetLocalTimer(AI_TIMER_KNOCKDOWN) && + (iTargetCurrentHP < i40 || iTargetPercentHP < i40))) + { + // If us, use harm self + if(oTarget == OBJECT_SELF) + { + // Innate ability. Under healing self, so leave as innate. + if(AI_ActionCastSpell(AI_SPELLABILITY_UNDEAD_HARM_SELF, FALSE, oTarget)) return TRUE; + } + // Use it...if we have it...and attack with a bow. + if(AI_ActionCastSpell(SPELL_HARM, SpellHostTouch, oTarget, i16, FALSE, ItemHostTouch)) return TRUE; + } + // Other Under things! + // Inflict range...always use top 2. + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_CRITICAL_WOUNDS, SpellHostTouch, oTarget)) return TRUE; + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_SERIOUS_WOUNDS, SpellOtherSpell, oTarget)) return TRUE; + // Circle of doom: d8 + Caster level heal. Category 1. + if(AI_ActionCastSpell(SPELL_CIRCLE_OF_DEATH, SpellHostRanged, oTarget, i15, TRUE, ItemHostRanged)) return TRUE; + // Negative Energy Burst...this is good enough to always use normally...same as Circle of doom! (d8 + caster) + if(AI_ActionCastSpell(SPELL_NEGATIVE_ENERGY_BURST, SpellHostRanged, oTarget, i13, TRUE, ItemHostRanged)) return TRUE; + // Lower ones ain't too good for some HD (ours!) + if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i12) + { + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MODERATE_WOUNDS, SpellOtherSpell, oTarget)) return TRUE; + if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i6) + { + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_LIGHT_WOUNDS, SpellOtherSpell, oTarget)) return TRUE; + if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i2) + { + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MINOR_WOUNDS, SpellOtherSpell, oTarget)) return TRUE; + } + } + } + // No undead healing cast + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: TalentHealingSelf +//:://///////////////////////////////////////////// + Uses the best it can. + 1. If it is heal, they need to be under half HP and under 40 HP + 2. If not, it has to be under half HP and not be heal/mass heal + 3. Testing to see if harm will be cast by undead + + 1.3 changes: A few bug fixes...not sure what was wrong before. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ +int AI_AttemptHealingSelf() +{ + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CURING, AI_OTHER_COMBAT_MASTER)) return FALSE; + + // Determine what we should heal at... + int iPercent = GetBoundriedAIInteger(AI_HEALING_US_PERCENT, i50, i100, i1); + + // What % are we at? It is GlobalOurPercentHP + // Are we under the right %? + if(GlobalOurPercentHP < iPercent) + { + // We can't be a silly race! + if(GlobalOurRace != RACIAL_TYPE_UNDEAD && + GlobalOurRace != RACIAL_TYPE_CONSTRUCT) + { + int nWillHeal; + // If we can heal self with feats...use them! No AOO + if(GetHasFeat(FEAT_WHOLENESS_OF_BODY) && + ((GlobalOurCurrentHP <= GetLevelByClass(CLASS_TYPE_MONK, OBJECT_SELF) * i2) || + GlobalOurPercentHP < i20)) + { + if(AI_ActionUseFeatOnObject(FEAT_WHOLENESS_OF_BODY)) return TRUE; + } + // Only use this on ourselves. + if(GetHasFeat(FEAT_LAY_ON_HANDS)) + { + // This does the actual formula...note, putting ones to stop DIVIDE BY ZERO errors + int nChr = GetAbilityModifier(ABILITY_CHARISMA); + if(nChr < i1) nChr = i1;// Error checking + int nLevel = GetLevelByClass(CLASS_TYPE_PALADIN); + if(nLevel < i1) nLevel = i1;// Error checking + //Caluclate the amount needed to be at, to use. + int nHeal = nLevel * nChr; + if(nHeal < i1) nHeal = i1;// Error checking + // We can be under the amount healed, or under 30 + if(GlobalOurCurrentHP < nHeal || + GlobalOurPercentHP < i30) + { + if(AI_ActionUseFeatOnObject(FEAT_LAY_ON_HANDS)) return TRUE; + } + } + // Note: Feat Lesser Bodily Adjustment uses cure light wounds spell script. + // Odd classes mean no potions. + int iPotions = TRUE; + if(GlobalOurRace == RACIAL_TYPE_ABERRATION || + GlobalOurRace == RACIAL_TYPE_BEAST || GlobalOurRace == RACIAL_TYPE_ELEMENTAL || + GlobalOurRace == RACIAL_TYPE_VERMIN || GlobalOurRace == RACIAL_TYPE_MAGICAL_BEAST || + GlobalOurRace == RACIAL_TYPE_DRAGON || GlobalOurRace == RACIAL_TYPE_ANIMAL) + iPotions = FALSE; + + // Lets see if we can use a healing kit! Only a valid race (as potions) + if(iPotions && GetAIInteger(AI_VALID_HEALING_KITS) && + !GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_USING_HEALING_KITS, AI_OTHER_COMBAT_MASTER)) + { + // Need a valid kit and skill to be worth using :-) + if(GetIsObjectValid(GlobalHealingKit) && + (GetSkillRank(SKILL_HEAL) >= (GlobalOurHitDice / i3) || + GlobalOurPercentHP < i30)) + { + AI_EquipBestShield(); + // 21: "[DCR:Casting] Healing self with healing kit, [Kit] " + GetName(GlobalHealingKit) + DebugActionSpeakByInt(21, GlobalHealingKit); + ActionUseSkill(SKILL_HEAL, OBJECT_SELF, i0, GlobalHealingKit); + return TRUE; + } + } + // Finally, heal self with potions, spells or whatever. + return AI_ActionHealObject(OBJECT_SELF); + } + else if(GlobalOurRace == RACIAL_TYPE_UNDEAD) + { + // Undead can cast some spells to heal... + return AI_ActionHealUndeadObject(OBJECT_SELF); + } + } + return FALSE; +} +// Get the nearest seen ally creature with the effect. +// - Checks us first +// - Then checks leader +// - Then loops seen allies within 20M. +object AI_GetNearestAllyWithEffect(int iEffectHex) +{ + // Check self + if(AI_GetAIHaveEffect(iEffectHex)) return OBJECT_SELF; + // Check leader + if(AI_GetAIHaveEffect(iEffectHex, GlobalNearestLeader)) return GlobalNearestLeader; + + // Check seen allies in 20M + int iCnt = i1; + object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oAlly) && GetDistanceToObject(oAlly) <= f20) + { + // Don't target undead + if(GetRacialType(oAlly) != RACIAL_TYPE_UNDEAD) + { + // Has it got the effect? + // - Stop and return oAlly. + if(AI_GetAIHaveEffect(iEffectHex, oAlly)) return oAlly; + } + // Carry on loop + iCnt++; + oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + } + return OBJECT_INVALID; +} +// Returns the nearest ally with iMin effects, and X more based on HD. +// - Checks us first. True if (HD/6) Effects and (iMin - 1) effects +// - Checks leader next. True if (HD/5) Effects and (iMin - 2) effects +// - Checks allies after. True if (HD/4) Effects and (iMin) effects +object AI_GetNearestAllyWithRestoreEffects(int iMin) +{ + // Check self /6 (need much less) + int iAmount = GetLocalInt(OBJECT_SELF, AI_ABILITY_DECREASE); + if(iAmount >= iMin - i1 && iAmount >= GlobalOurHitDice / i6) return OBJECT_SELF; + // Check leader - /5 (need less) + if(GlobalValidLeader) + { + iAmount = GetLocalInt(GlobalNearestLeader, AI_ABILITY_DECREASE); + if(iAmount >= iMin - i2 && iAmount >= GetHitDice(GlobalNearestLeader) / i5) return GlobalNearestLeader; + } + // Check seen allies in 20M - until we get one with /2 effects, minimum 4. + int iCnt = i1; + object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oAlly) && GetDistanceToObject(oAlly) <= f20) + { + // Don't target undead + if(GetRacialType(oAlly) != RACIAL_TYPE_UNDEAD) + { + // Check ally + iAmount = GetLocalInt(oAlly, AI_ABILITY_DECREASE); + if(iAmount >= iMin && iAmount >= GetHitDice(oAlly) / i4) return oAlly; + } + // Carry on loop + iCnt++; + oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + } + return OBJECT_INVALID; +} +/*:://///////////////////////////////////////////// +//:: Name: TalentCureCondition +//:://///////////////////////////////////////////// + Uses spells to cure conditions. Needs to be checked fully + - Uses allies own integers to check thier effects + - Loops spells (Best => worst) and if we havn't cast it in an amount of time + we check effects (Us, leader, allies seen) until we find someone (nearest) + who we can cast it on. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptCureCondition() +{ + // Set OnSpawn if we have any of the spells we use. + if(!GetSpawnInCondition(AI_VALID_CURE_CONDITION_SPELLS, AI_VALID_SPELLS) || + GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_CURING_ALLIES, AI_OTHER_MASTER) || + GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CURING, AI_OTHER_COMBAT_MASTER)) return FALSE; + + // Check timer + if(GetLocalTimer(AI_TIMER_CURE_CONDITION)) return FALSE; + + int iTimer = GetAIInteger(SECONDS_BETWEEN_STATUS_CHECKS); + if(iTimer >= i1) + { + // Set timer + SetLocalTimer(AI_TIMER_CURE_CONDITION, IntToFloat(iTimer)); + } + + // Cure spells in order: + // SPELL_GREATER_RESTORATION - Ability Decrease, AC decrease, Attack decrease, + // Damage Decrease, Damage Immunity Decrease, Saving Throw Decrease, Spell + // resistance Decrease, Skill decrease, Blindness, Deaf, Curse, Disease, Poison, + // Charmed, Dominated, Dazed, Confused, Frightened, Negative level, Paralyze, + // Slow, Stunned. + // SPELL_FREEDOM - Paralyze, Entangle, Slow, Movement speed decrease. (+Immunity!) + // SPELL_RESTORATION - Ability Decrease, AC decrease, Attack Decrease, + // Damage Decrease, Damage Immunity Decrease, Saving Throw Decrease, + // Spell Resistance Decrease, Skill Decrease, Blindess, Deaf, Paralyze, Negative level + // SPELL_REMOVE_BLINDNESS_AND_DEAFNESS - Blindess, Deaf. + // SPELL_NEUTRALIZE_POISON - Poison + // SPELL_REMOVE_DISEASE - Disease + // SPELL_REMOVE_CURSE - Curse + // SPELL_LESSER_RESTORATION - Ability Decrease, AC decrease, Attack Decrease, + // Damage Decrease, Damage Immunity Decrease, Saving throw Decrease, + // Spell Resistance Decrease, Skill decrease. + + // Note: We like to dispel anything that stuns us - EG paralyze effects, + // stunning, confusion, dazedness VIA Greater Restoration, after that, + // blindness (A great inhibiter) and then poison disease and curse. + + // Lastly, ability damage ETC can come in after the stuns, or right at + // the end, depending on the amount. + object oHeal; + // Petrify! + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectPetrify); + if(GetIsObjectValid(oHeal)) + { + // Only one + if(AI_ActionCastSpell(SPELL_STONE_TO_FLESH, FALSE, oHeal)) return TRUE; + } + // First, Paralysis should be removed. + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectParalyze); + if(GetIsObjectValid(oHeal)) + { + // Freedom first + if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Daze + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectDazed); + if(GetIsObjectValid(oHeal)) + { + // Freedom first + if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Next? blindness + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectBlindness); + if(GetIsObjectValid(oHeal)) + { + // Remove first + if(AI_ActionCastSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Lots of negative effects. + oHeal = AI_GetNearestAllyWithRestoreEffects(i7); + if(GetIsObjectValid(oHeal)) + { + // Only these can remove ability decreases ETC. + if(AI_ActionCastSpell(SPELL_LESSER_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Negative level is tough + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectNegativeLevel); + if(GetIsObjectValid(oHeal)) + { + // Only these can remove it + if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Slow + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectSlowed); + if(GetIsObjectValid(oHeal) && !AI_GetAIHaveEffect(GlobalEffectHaste, oHeal)) + { + // haste - we don't set we have this OnSpawn, because if we have not + // got any other healing cure spells, we're probably not meant to cast it on slowed people. + if(AI_ActionCastSpell(SPELL_MASS_HASTE, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_HASTE, FALSE, oHeal)) return TRUE; + } + // Entangle/Move slowdown + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectEntangle); + if(!GetIsObjectValid(oHeal)) + { + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectMovementSpeedDecrease); + } + if(GetIsObjectValid(oHeal)) + { + // Freedom first + if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Deafness + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectDeaf); + if(GetIsObjectValid(oHeal)) + { + // Remove first + if(AI_ActionCastSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Quite a few negative effects. + oHeal = AI_GetNearestAllyWithRestoreEffects(i5); + if(GetIsObjectValid(oHeal)) + { + // Only these can remove ability decreases ETC. + if(AI_ActionCastSpell(SPELL_LESSER_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Curse + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectCurse); + if(GetIsObjectValid(oHeal)) + { + // Remove first + if(AI_ActionCastSpell(SPELL_REMOVE_CURSE, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Disease + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectDisease); + if(GetIsObjectValid(oHeal)) + { + // Remove first + if(AI_ActionCastSpell(SPELL_REMOVE_DISEASE, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // Poison + oHeal = AI_GetNearestAllyWithEffect(GlobalEffectPoison); + if(GetIsObjectValid(oHeal)) + { + // Remove first + if(AI_ActionCastSpell(SPELL_NEUTRALIZE_POISON, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + // See if there is anyone with a decent amount of effects. + // Some negative effects. + oHeal = AI_GetNearestAllyWithRestoreEffects(i3); + if(GetIsObjectValid(oHeal)) + { + // Only these can remove ability decreases ETC. + if(AI_ActionCastSpell(SPELL_LESSER_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE; + } + return FALSE; +} + +/*:://///////////////////////////////////////////// +//:: Name: TalentHeal +//:://///////////////////////////////////////////// + HEAL ALL ALLIES + Only if they are in sight, and are under a percent%. Always nearest... +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptHealingAlly() +{ + // Are we able to raise allies? + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_WILL_RAISE_ALLIES_IN_BATTLE, AI_OTHER_COMBAT_MASTER) && SpellConSinTar) + { + // Leader - if dead, make sure we raise them! + if(GetIsObjectValid(GlobalNearestLeader) && + GetIsDead(GlobalNearestLeader) && + !GetLocalInt(GlobalNearestLeader, AI_INTEGER + I_AM_TOTALLY_DEAD)) + { + // Talent 7 - Conditional single. + if(AI_ActionCastSpell(SPELL_RESURRECTION, SpellConSinTar, GlobalNearestLeader, i17, FALSE, ItemConSinTar)) return TRUE; + if(AI_ActionCastSpell(SPELL_RAISE_DEAD, SpellConSinTar, GlobalNearestLeader, i15, FALSE, ItemConSinTar)) return TRUE; + } + // Get the nearest ally, seen, who is dead + // - This can cause problems as no looping dead people, as there is a + // "totally dead" flag, used for bioware's lootable corpses. + object oDead = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, FALSE, + OBJECT_SELF, i1, + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + if(GetIsObjectValid(oDead) && !GetIgnore(oDead) && + !GetLocalInt(oDead, AI_INTEGER + I_AM_TOTALLY_DEAD)) + { + if(AI_ActionCastSpell(SPELL_RESURRECTION, SpellConSinTar, oDead, i17, FALSE, ItemConSinTar)) return TRUE; + if(AI_ActionCastSpell(SPELL_RAISE_DEAD, SpellConSinTar, oDead, i15, FALSE, ItemConSinTar)) return TRUE; + } + } + // Do we heal allies? + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_ONLY_CURE_SELF, AI_OTHER_COMBAT_MASTER) && + !GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CURING, AI_OTHER_COMBAT_MASTER)) + { + // We always heal leaders + // - Might make % HP checks. + if(GlobalValidLeader) + { + if(AI_ActionHealObject(GlobalNearestLeader)) return TRUE; + } + // Else we may heal a set ally + else if(GlobalValidSeenAlly) + { + // We run through a loop, and get who we might heal ;-) + int iNeedToBeAt = GetBoundriedAIInteger(AI_HEALING_ALLIES_PERCENT, i60, i100, i1); + object oAllyToHeal, oLoopTarget; + int iCnt, iPercentHitPoints, iChosenPercentHitPoints; + float fDistance, fChosenLastDistance; + // iSummonHeal will be TRUE to heal associates only if we have <= 3 allies in total + int iSummonHeal = FALSE; + if(GlobalTotalAllies <= i3) + { + iSummonHeal = TRUE; + } + fChosenLastDistance = f60;// So 1st target in health band gets picked + iCnt = i1; + oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oLoopTarget)) + { + if(GetRacialType(oLoopTarget) != RACIAL_TYPE_CONSTRUCT && + (GetAssociateType(oLoopTarget) == ASSOCIATE_TYPE_NONE || iSummonHeal == TRUE)) + { + // We do actually not ALWAYS use the nearest dead person, nor + // the most damaged in 20M or so. We use a mixture - the most + // damaged in a cirtain range. + iPercentHitPoints = AI_GetPercentOf(GetCurrentHitPoints(oLoopTarget), GetMaxHitPoints(oLoopTarget)); + // Do we consider them needing healing? + if(iPercentHitPoints <= iNeedToBeAt) + { + // Distance to them - we may not just heal the nearest under 60% + fDistance = GetDistanceToObject(oLoopTarget); + if(fDistance < fChosenLastDistance || + (fDistance < (fChosenLastDistance + f5) && + iPercentHitPoints < iChosenPercentHitPoints - i10)) + { + oAllyToHeal = oLoopTarget; + fChosenLastDistance = fDistance; + iChosenPercentHitPoints = iPercentHitPoints; + } + } + } + iCnt++; + oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + } + // Did we find someone to heal? + if(iChosenPercentHitPoints > i0) + { + int iAllyHealRace = GetRacialType(oAllyToHeal); + // Undead - negative energy heals! + if(iAllyHealRace == RACIAL_TYPE_UNDEAD) + { + // Stop now whatever, true or false. + return AI_ActionHealUndeadObject(oAllyToHeal); + } + else //if(iAllyHealRace != RACIAL_TYPE_CONSTRUCT) // Checked earlier + { + if(AI_ActionHealObject(oAllyToHeal)) + { + return TRUE; + } + // This will, if they have any (Standard ones! unless you add them in) + // healing potions, they will, if close and no cleric, pass them to + // people who need them. It uses ActionGiveItem so may move to target. + // There are extra speakstrings for these events. + else if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GIVE_POTIONS_TO_HELP, AI_OTHER_COMBAT_MASTER) && + // Can they use potions anyway? + iAllyHealRace != RACIAL_TYPE_ABERRATION && iAllyHealRace != RACIAL_TYPE_BEAST && + iAllyHealRace != RACIAL_TYPE_ELEMENTAL && iAllyHealRace != RACIAL_TYPE_VERMIN && + iAllyHealRace != RACIAL_TYPE_MAGICAL_BEAST && iAllyHealRace != RACIAL_TYPE_DRAGON && + iAllyHealRace != RACIAL_TYPE_ANIMAL) + { + // So we pass over a potion. Obviously, we do not need healing, + // so any potion is good! + // NW_IT_MPOTION001 = Light + // NW_IT_MPOTION002 = Serious + // NW_IT_MPOTION003 = Critical + // NW_IT_MPOTION012 = Heal + // NW_IT_MPOTION020 = Moderate + + // Do we have any of the above? Eh? + // Heal + object oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION012"); + if(!GetIsObjectValid(oPotionToPass)) + { + // Critical + oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION003"); + if(!GetIsObjectValid(oPotionToPass)) + { + // Serious + oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION002"); + if(!GetIsObjectValid(oPotionToPass)) + { + // Moderate + oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION020"); + if(!GetIsObjectValid(oPotionToPass)) + { + // Light + oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION001"); + if(!GetIsObjectValid(oPotionToPass)) + { + // If NONE are valid, we delete this spawn in condition + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_GIVE_POTIONS_TO_HELP, AI_OTHER_COMBAT_MASTER); + // Stop - passing potions is last thing to try + return FALSE; + } + } + } + } + } + // If any valid, we pass it. + if(GetIsObjectValid(oPotionToPass)) + { + // Pass it over with ActionGiveItem + ActionGiveItem(oPotionToPass, oAllyToHeal); + // Speak arrays. + string sSpeak = GetLocalString(OBJECT_SELF, AI_TALK_WE_PASS_POTION + s1); + if(sSpeak != "") + { + SpeakString(sSpeak); + } + sSpeak = GetLocalString(OBJECT_SELF, AI_TALK_WE_GOT_POTION + s1); + if(sSpeak != "") + { + AssignCommand(oAllyToHeal, SpeakString(sSpeak)); + } + return TRUE; + } + } + } + } + } + } + return FALSE; +} + +/*:://///////////////////////////////////////////// +//:: Name: DoSummonFamiliar +//:://///////////////////////////////////////////// + This will, if it can, summon a familiar, or + animal companion, and if set to want to. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptFeatSummonFamiliar() +{ + if(GetSpawnInCondition(AI_FLAG_COMBAT_SUMMON_FAMILIAR, AI_COMBAT_MASTER)) + { + if(GetHasFeat(FEAT_SUMMON_FAMILIAR) && !GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_FAMILIAR))) + { + // 1: "[DCR:Feat] Summoning my familiar" + DebugActionSpeakByInt(22); + // We do not bother turning off hiding/searching + ActionUseFeat(FEAT_SUMMON_FAMILIAR, OBJECT_SELF); + return TRUE; + } + else if(GetHasFeat(FEAT_ANIMAL_COMPANION) && !GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION))) + { + // 23: "[DCR:Feat] Summoning my animal companion" + DebugActionSpeakByInt(23); + ActionUseFeat(FEAT_ANIMAL_COMPANION, OBJECT_SELF); + return TRUE; + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name AI_ActionFleeScared +//:://///////////////////////////////////////////// + Makes the person move away from the nearest enemy, or the defined target. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:: Created On: 13/01/03 +//:://///////////////////////////////////////////*/ +void AI_ActionFleeScared() +{ + // Sets to flee mode + SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE); + SpeakArrayString(AI_TALK_ON_STUPID_RUN); + ClearAllActions(); + // 24: "[DCR:Fleeing] Stupid/Panic/Flee moving from enemies/position - We are a commoner/no morale/failed < 3 int" + DebugActionSpeakByInt(24); + + // Get a target, or run from position + object oTarget = GlobalNearestEnemySeen; + if(!GetIsObjectValid(oTarget)) + { + oTarget = GlobalNearestEnemyHeard; + if(!GetIsObjectValid(oTarget)) + { + oTarget = GetLastHostileActor(); + if(!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) + { + // Away from position + ActionMoveAwayFromLocation(GetLocation(OBJECT_SELF), TRUE, f50); + return;// Stop, no move away from enemy + } + } + } + ActionMoveAwayFromObject(oTarget, TRUE, f50); +} +/*:://///////////////////////////////////////////// +//:: Name Flee +//:://///////////////////////////////////////////// + Makes checks, and may flee. + + SpawnInCondition(TURN_OFF_GROUP_MORALE, AI_TARGETING_FLEE_MASTER); + // This turns OFF any sort of group morale bonuses. + + + SpawnInCondition(FLEE_TO_NEAREST_NONE_SEEN, AI_TARGETING_FLEE_MASTER); + // This is a simple thing, and will, instead of moving to the best + // group of allies, will just move to the nearest non-seen and non-heard + // ally. This may help resources...if fleeing is activated. + + SpawnInCondition(FLEE_TO_OBJECT, AI_TARGETING_FLEE_MASTER); + // This will flee to an object, with the correct tag. + // ONLY if they are not there already, of course. Gets nearest (but if + // not valid, any one). This will get any object - placeables, monsters, + // well - anything! Also, waypoints, so this is easily useful. + // Can be dynamic: Instead of "BOS.." it could be GetTag(OBJECT_SELF) + "_FLEE" or something + // Ideas: Fleeing to thier leader, fleeing to a cage or a door (can be other area). + + LocalString(OBJECT_SELF, AI_FLEE_OBJECT, "BOSS_TAG_OR_WHATEVER"); + // This needs setting if the above is to work. + +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ +// Only true if we have a flee object. +int AI_ActionFlee(string sArray) +{ + object oToFlee = AI_GetNearbyFleeObject(); + if(GetIsObjectValid(oToFlee)) + { + // Speak string... + if(sArray != "") SpeakArrayString(sArray); + // Set it so we will not return for a bit. + SetAIObject(AI_FLEE_TO, oToFlee); + // Sets to flee mode + SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE); + // 25: [DCR:Fleeing] Fleeing to allies. [ID Array] " + sArray + " [Ally] " + GetName(oToFlee) + DebugActionSpeakByInt(25, oToFlee, FALSE, sArray); + // Move too them. + ActionMoveToObject(oToFlee, TRUE); + return TRUE; + } + return FALSE; +} +// Fleeing - set OnSpawn for setup. Uses a WILL save! +// - Bonuses in groups +// - May go get help +// - Won't run on a CR/HD threshold. +// - Leaders help! And all is intelligence based. +int AI_AttemptMoraleFlee() +{ + int iMoraleValue = GetAIInteger(AI_MORALE); + // Commoner flee + if(iMoraleValue < FALSE) + { + AI_ActionFleeScared(); + return TRUE; + } + // Declare vaiables... + // We only flee every few rounds, or at least check too! + // - This is set On Spawn if we are fearless by race or immunity + if(!GetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER) && + // - Its on a timer. + (!GetLocalTimer(AI_TIMER_FLEE) || + // - We check each round if under 40 and under 10% HP + (GlobalOurCurrentHP <= i40 && GlobalOurPercentHP <= i10))) + { + // Re-set timer even if we don't do the morale check. + if(!GetLocalTimer(AI_TIMER_FLEE)) + { + SetLocalTimer(AI_TIMER_FLEE, f20); + } + // Bosses, when they flee, make others flee. + int iBoss = GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER); + // Specials first: Never fight against impossible odds...or leader sense. + if(GlobalAverageEnemyHD > GlobalOurHitDice + i8) + { + if(GetSpawnInCondition(AI_FLAG_FLEEING_NEVER_FIGHT_IMPOSSIBLE_ODDS, AI_TARGETING_FLEE_MASTER) || iBoss) + { + // You see, if there is no valid object ,we stop the script so not to call GetNearbyFleeObject more then once + if(AI_ActionFlee(AI_TALK_ON_MORALE_BREAK)) + { + if(iBoss) + { + // AI command - makes everyone flee. + AISpeakString(LEADER_FLEE_NOW); + } + return TRUE; + } + else + { + return FALSE; + } + } + } + // HP check + int iHPBeAt = GetBoundriedAIInteger(HP_PERCENT_TO_CHECK_AT, i80, i100, i1); + // Do the check + if(GlobalOurPercentHP <= iHPBeAt || + // Either HP OR one of these: + (!GetSpawnInCondition(AI_FLAG_FLEEING_NO_OVERRIDING_HP_AMOUNT, AI_TARGETING_FLEE_MASTER) && + // - If the enemy is very strong, we do a will save (8 HD over us) + (GlobalAverageEnemyHD > GlobalOurHitDice + i8 || + // - If we have a morerate morale penalty (Set by massive damage/many deaths around us) + // - We are alone + GetAIInteger(AI_MORALE_PENALTY) >= i10 || + GlobalTotalAllies < i1))) + { + // Difference of HD to cancle out check... + int iDifference = GetBoundriedAIInteger(AMOUNT_OF_HD_DIFFERENCE_TO_CHECK, -2, -50, i50);// Default to -2. + int iAlliesBonus = FALSE; + // We add X amount per ally present. + // - We can add up to a minimum of 6, maximum of our hit dice/2. + if(!GetSpawnInCondition(AI_FLAG_FLEEING_TURN_OFF_GROUP_MORALE, AI_TARGETING_FLEE_MASTER) && + GlobalTotalAllies >= i1) + { + if(GlobalTotalAllies > i6) + { + iAlliesBonus = i6; + } + else if(iAlliesBonus > GlobalOurHitDice/i2) + { + iAlliesBonus = GlobalOurHitDice/i2; + } + else + { + iAlliesBonus = GlobalTotalAllies; + } + iMoraleValue += iAlliesBonus; + } + // Double value if leader present! + if(GlobalValidLeader) iMoraleValue *= i2; + // Check... + if((GlobalOurHitDice - iDifference) < GlobalAverageEnemyHD) + { + // Save DC from 0 to 50. Default 20. This is the base. + int iSaveDC = GetBoundriedAIInteger(BASE_MORALE_SAVE, i20, i100, i1); + // Change it as with our leader, morale and group morale bonuses. + iSaveDC -= iMoraleValue; + // Note we will add DC based on difference in HD between us + // and our Global Melee Target (as the fact is, it is normally + // the nearest, and is always valid. Also, if you are fighting + // something you can defeat (EG: Badger summon) you don't run, + // but you will run against the level 20 mage who summoned it, if + // you were levle 5 or so. + + // Add thiers to DC, take ours. Basically Ours - Theres added on. + iSaveDC += GetHitDice(GlobalMeleeTarget); + iSaveDC -= GlobalOurHitDice; + + // If we have a save DC of over 1...we check! + if(iSaveDC >= i1) + { + if(!WillSave(OBJECT_SELF, iSaveDC, SAVING_THROW_TYPE_FEAR, GlobalMeleeTarget)) + { + // If we fail the will save, VS fear... + // You see, if there is no valid object ,we stop the script so not to call GetNearbyFleeObject more then once + if(AI_ActionFlee(AI_TALK_ON_MORALE_BREAK)) + { + return TRUE; + } + // Intelligence 1-3 runs when we fail and no allies. + // 4+ means we don't run, and stay and fight (IE FALSE) + else if(GlobalIntelligence <= i3) + { + // Stupid run + AI_ActionFleeScared(); + return TRUE; + } + else + // Higher intelligence don't run from them, but does say something + { + SpeakArrayString(AI_TALK_ON_CANNOT_RUN); + } + return FALSE; + } + } + } + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: GoForTheKill +//:://///////////////////////////////////////////// + Very basic at the moment. This will, if thier inteligence is + high and they can be hit relitivly easily, and at low HP + will attack in combat. May add some spells, if I can be bothered. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptGoForTheKill() +{ + // Turn off script + if(GetSpawnInCondition(AI_FLAG_COMBAT_NO_GO_FOR_THE_KILL, AI_COMBAT_MASTER) || + GlobalIntelligence <= i8) + { + return FALSE; + } + int nSleepingOnly = FALSE; + // Finnish off a dead PC, or dying one, out of combat. + object oTempEnemy = GetNearestCreature( + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + OBJECT_SELF, i1, + CREATURE_TYPE_IS_ALIVE, FALSE, + CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + // Check if valid. If not, check for one with the spell "sleep" on them. + if(!GetIsObjectValid(oTempEnemy)) + { + // This means we only attack in melee (no spells, no ranged) + nSleepingOnly = TRUE; + oTempEnemy = GetNearestCreature( + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + OBJECT_SELF, i1, + CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_SLEEP, + CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + // Dragon sleep breath + if(!GetIsObjectValid(oTempEnemy)) + { + oTempEnemy = GetNearestCreature( + CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + OBJECT_SELF, i1, + CREATURE_TYPE_HAS_SPELL_EFFECT, SPELLABILITY_DRAGON_BREATH_SLEEP, + CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + } + } + float fDistance = GetDistanceToObject(oTempEnemy); + // Valid, and is not "dead dead" + if(GetIsObjectValid(oTempEnemy) && + // -11 is TOTALLY dead. + GetCurrentHitPoints(oTempEnemy) > iM10 && + // Intelligence 9+. 10 will attack with many enemies, 9 with 1 or under + ((GlobalMeleeAttackers <= i3 && + GlobalIntelligence >= i10) || + GlobalMeleeAttackers <= i1) && + // Make sure it isn't too far, and they are only sleeping + (nSleepingOnly == FALSE || + fDistance < f6) && + // Big AC check, but mages with +1, VS ac of 30 won't hit :-) + GlobalOurBaseAttackBonus >= GetAC(oTempEnemy) - i25) + { + // 26: "[DCR:GFTK] Attacking a PC who is dying/asleep! [Enemy]" + GetName(oTempEnemy) + DebugActionSpeakByInt(26, oTempEnemy); + // Attempt default most damaging to try and do most damage. + if(fDistance > GlobalRangeToMeleeTarget) + { + ActionEquipMostDamagingRanged(oTempEnemy); + } + else + { + ActionEquipMostDamagingMelee(oTempEnemy); + } + ActionAttack(oTempEnemy); + return TRUE; + } + + // Now, we check for most damaged member of the nearest enemies faction. + // - Uses bioware function - just...simpler + // - Only with 9+ int + oTempEnemy = GetFactionMostDamagedMember(GlobalMeleeTarget); + // - Effect, if lots of people can attack this, like with ranged things, + // it can lead to some bloody deaths, especially for some classes. + // - Note: Intelligence of 9+ shouldn't be used for low levels! + if(GetIsObjectValid(oTempEnemy)) + { + // From 1-6, or 1-10 HP they need. + int iHP = GetCurrentHitPoints(oTempEnemy); + int iBeBelow = i6 + Random(i4); + int iHTH = FALSE;// TRUE = melee + fDistance = GetDistanceToObject(oTempEnemy); + // If in melee range, we set to melee, and add stength to what we can knock off! + if(fDistance <= f4) + { + iHTH = TRUE; + iBeBelow += GetAbilityModifier(ABILITY_STRENGTH); + } + // Check... + if(GetCurrentHitPoints(oTempEnemy) <= iBeBelow && + GetMaxHitPoints(oTempEnemy) >= i20) + { + // We also need to check if we can hit them to kill them! + // - Either high BAB against thier AC (take roll as 15+) + if(GlobalOurBaseAttackBonus >= GetAC(oTempEnemy) - i15 || + // - or it is 0.75 of our hit dice (IE cleric or better). + GlobalOurBaseAttackBonus >= ((GetHitDice(oTempEnemy) * i3) / i2)) + { + if(iHTH) + { + ActionEquipMostDamagingMelee(oTempEnemy); + } + // We take a big risk here... we equip a ranged (default call) + // not how good it is, things in the way, or + // if we don't have ammo or even have it! + // - EquipsMelee Anyway. + // - Still needs to be valid, else we do normal actions. + else if(GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED))) + { + ActionEquipMostDamagingRanged(oTempEnemy); + } + else // If we have no ranged wepaon, ignore this. + { + return FALSE; + } + ActionAttack(oTempEnemy); + return TRUE; + } + // Else, we do spells! Just a few... + else + { + // This stops us being unprotected, as there may be melee attackers. + if(!iHTH || GlobalMeleeAttackers < i3) + { + // Spells are done with little consideration for immunities. + // - Saves power, and shows fury to knock out. + // - All auto-hit, no-save spells. + // - No items. + // - Checks immunity level, incase of globes. + int iImmuneLevel = AI_GetSpellLevelEffect(oTempEnemy); + + // Issacs storms are long range beasts! + // Greater Missile Storm. Level 6 (Wizard). 2d6 * Caster level dice up to 20, divided between targets. + if(AI_ActionCastSpell(SPELL_ISAACS_GREATER_MISSILE_STORM, SpellHostRanged, oTempEnemy, i16)) return TRUE; + + if(iImmuneLevel < i4) + { + // Lesser Missile Storm. Level 4 (Wizard). 1d6 * Caster level dice up to 10, divided between targets. + if(AI_ActionCastSpell(SPELL_ISAACS_LESSER_MISSILE_STORM, SpellHostRanged, oTempEnemy, i14)) return TRUE; + + if(iImmuneLevel < i3) + { + // Searing light. Level 3 (Cleric). Full level: 1-10d6 VS undead. Half Level: 1-5d6 VS constructs. 1-5d8 VS Others. + if(AI_ActionCastSpell(SPELL_SEARING_LIGHT, SpellHostRanged, oTempEnemy, i13)) return TRUE; + + if(iImmuneLevel < i2) + { + // Acid arrow. Level 2 (Wizard). 3d6 damage on hit. 1d6 for more rounds after. + if(AI_ActionCastSpell(SPELL_MELFS_ACID_ARROW, SpellHostRanged, oTempEnemy, i12)) return TRUE; + + if(iImmuneLevel < i1) + { + // Magic missile. Good knockout spell. Shield stops it. + if(!GetHasSpellEffect(SPELL_SHIELD, oTempEnemy)) + { + // Magic Missile. Level 1 (Wizard). 1-5 missiles, 1-9 levels. 1d4+1 damage/missile. + if(AI_ActionCastSpell(SPELL_MAGIC_MISSILE, SpellHostRanged, oTempEnemy, i11)) return TRUE; + } + + // Negative energy ray - note will save! + if(fDistance < fMediumRange && + !AI_SaveImmuneAOE(oTempEnemy, SAVING_THROW_WILL, i1)) + { + // Negative Energy Ray. Level 1 (Mage) 2 (Cleric) 1d6(CasterLevel/2) to 5d6 negative damage. + if(AI_ActionCastSpell(SPELL_NEGATIVE_ENERGY_RAY, SpellHostRanged, oTempEnemy, i11)) return TRUE; + } + } + } + } + } + } + } + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name: AbilityAura +//:://///////////////////////////////////////////// + This will use all abilties. + + Note: + + - They are cheat cast. If removed, as DMG, they are instantly re-applied as + a free action! + - They are cast instantly. + - +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +void AI_AttemptCastAuraSpell(int iSpellID) +{ + if(GetHasSpell(iSpellID) && !GetHasSpellEffect(iSpellID)) + { + // Cheat/fast cast the aura. + ActionCastSpellAtObject(iSpellID, OBJECT_SELF, METAMAGIC_NONE, TRUE, i20, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + } +} + +void AI_ActionAbilityAura() +{ + if(SpellAura) + { + int iCnt; + // We can loop around a few: + // 195 AURA_BLINDING + // 196 Aura_Cold + // 197 Aura_Electricity + // 198 Aura_Fear + // 199 Aura_Fire + // 200 Aura_Menace + // 201 Aura_Protection + // 202 Aura_Stun + // 203 Aura_Unearthly_Visage + // 204 Aura_Unnatural + for(iCnt = SPELLABILITY_AURA_BLINDING; iCnt <= SPELLABILITY_AURA_UNNATURAL; iCnt++) + { + // Wrapper function. + AI_AttemptCastAuraSpell(iCnt); + } + // Other auras we would have unlimited uses in + AI_AttemptCastAuraSpell(SPELLABILITY_DRAGON_FEAR); + AI_AttemptCastAuraSpell(SPELLABILITY_TYRANT_FOG_MIST); + AI_AttemptCastAuraSpell(AI_SPELLABILITY_AURA_OF_HELLFIRE); + + // Then, ones that are limited duration, or limited uses. + if(!GetHasSpellEffect(SPELLABILITY_MUMMY_BOLSTER_UNDEAD) && + GetHasSpell(SPELLABILITY_MUMMY_BOLSTER_UNDEAD)) + { + ActionCastSpellAtObject(SPELLABILITY_MUMMY_BOLSTER_UNDEAD, OBJECT_SELF); + } + if(!GetHasSpellEffect(SPELLABILITY_EMPTY_BODY) && GetHasFeat(FEAT_EMPTY_BODY)) + { + ActionUseFeat(FEAT_EMPTY_BODY, OBJECT_SELF); + } + } +} +/*:://///////////////////////////////////////////// +//:: Name LeaderActions +//:://///////////////////////////////////////////// + This will make the leader "command" allies. Moving + one to get others, orshout attack my target. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +void AI_ActionLeaderActions() +{ + // We only advance if we are the group leader. + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER)) + { + // Does the enemy out class us by a random amount? (2-6 HD difference). + // Sends a runner. + if(GlobalAverageEnemyHD > (GlobalAverageFriendlyHD + d4() + i2)) + { + if(GlobalValidAlly && !GetLocalTimer(AI_TIMER_LEADER_SENT_RUNNER)) + { + object oBestAllies = AI_GetNearbyFleeObject(); + if(GetIsObjectValid(oBestAllies)) // Send a runner... + { + // Speak array. + SpeakArrayString(AI_TALK_ON_LEADER_SEND_RUNNER); + // All we need to do is set an action on the nearest ally, + // and the object to run to. + SetLocalObject(GlobalNearestAlly, AI_OBJECT + AI_RUNNER_TARGET, oBestAllies); + // Set action. + SetLocalInt(GlobalNearestAlly, AI_CURRENT_ACTION, AI_SPECIAL_ACTIONS_ME_RUNNER); + // Don't send another for 50 seconds. + SetLocalTimer(AI_TIMER_LEADER_SENT_RUNNER, f50); + } + } + } + // Second, we shout for people to target this person - IF we need help + // - Example, low HP us, high HP them. + // - Uses melee target. + int iLeaderCountForShout = GetAIInteger(AI_LEADER_SHOUT_COUNT); + iLeaderCountForShout++; + if(iLeaderCountForShout > i4) + { + // Check HD difference + if(GetHitDice(GlobalMeleeTarget) - i5 > GlobalAverageEnemyHD) + { + // Set who we want them to attack. + SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, GlobalMeleeTarget); + // Shout counter re-set + iLeaderCountForShout = FALSE; + // Speak random custom shout (like "You lot, attack this one!") + SpeakArrayString(AI_TALK_ON_LEADER_ATTACK_TARGET); + + // Speak a silent AI shout + AISpeakString(LEADER_ATTACK_TARGET); + } + } + SetAIInteger(AI_LEADER_SHOUT_COUNT, iLeaderCountForShout); + } +} +/*:://///////////////////////////////////////////// +//:: Name ArcherRetreat +//:://///////////////////////////////////////////// + This may make the archer retreat - if they are set to, and have a ranged weapon + and don't have point blank shot, and has a nearby ally. (INT>=1 if set to does + it more if higher). +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptArcherRetreat() +{ + //SetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER); + // This is quite specifically for archers - ones good at using bows. + // They will, quite often (with support from others) retreat to use + // bows much better (IE no AOO). they perfere ranged combat, and only + // draw HTH weapons (If they have any, else they use thier bow) in the end. + // Note: Below, you can set it to ALWAYs move back, if an archer. + //SetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER); + + // Many conditions - we must have it to start! + if(GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER) || + GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER)) + { + // Enemy range must be close (HTH distance, roughly) and we must have + // a close ally. ELSE we'll just get lots of AOO and die running (pointless) + if((GlobalEnemiesIn4Meters > i1 && GlobalValidAlly && GlobalRangeToAlly < f4) || + GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER)) + { + // We may, if at the right range (and got a ranged weapon) equip it. + object oRanged = GetAIObject(AI_WEAPON_RANGED); + if(GetIsObjectValid(oRanged) && + GetItemPossessor(oRanged) == OBJECT_SELF) + { + // Is the ranged weapon valid? And iRangedAttack == TRUE + // We need valid ammo, else we eqip nothing! + int iValidAmmo = GetAIInteger(AI_WEAPON_RANGED_AMMOSLOT); + if(iValidAmmo == INVENTORY_SLOT_RIGHTHAND || + GetIsObjectValid(GetItemInSlot(iValidAmmo))) + { + // 27: "[DCR:Moving] Archer Retreating back from the enemy [Enemy]" + GetName(GlobalMeleeTarget) + DebugActionSpeakByInt(27, GlobalMeleeTarget); + // - Equip the rnaged weapon if not already + if(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND) != oRanged) + { + ActionEquipItem(oRanged, INVENTORY_SLOT_RIGHTHAND); + } + ActionMoveAwayFromObject(GlobalMeleeTarget, TRUE, f15); + return TRUE; + } + } + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name UseTurning +//:://///////////////////////////////////////////// + This, if we have the feat, will turn things it can. + Need to really make sure it works well, and maybe + make an interval between one and another. +//:://///////////////////////////////////////////// +//:: Created By: Bioware. Edited by Jasperre. +//::////////////////////////////////////////////*/ +int AI_AttemptFeatTurning() +{ + // We loop the targets and see who we come up with to hit! + // - Use ranged enemy checks, seen/heard and only if we have any + // valid races in the area. + if(GetHasFeat(FEAT_TURN_UNDEAD)) + { + // The turn level is set OnSpawn if we have FEAT_TURN_UNDEAD + int nTurnLevel = GetAIInteger(AI_TURNING_LEVEL); + // Error checking + if(nTurnLevel <= i0) return FALSE; + + // We then loop through targets nearby, up to 20M. We can easily use + // GetHasSpellEffect(AI_SPELL_FEAT_TURN_UNDEAD) + // and for the epic feat + // GetHasSpellEffect(AI_SPELL_FEAT_PLANAR_TURNING) + + // Ok, so how does turning work? + // - Basically, all undead/virmin/elementals or outsiders are turned, + // with constructs being damaged. + // - Works agasints non-friends. + // - If we have nClassLevel/2 >= nHD (nHD = GetHitDice(oTarget) + GetTurnResistanceHD(oTarget) + // then kill, else turn. + // - Only can turn if nHD <= nTurnLevel. + // - 20M range. + // Flags for bonus turning types + int nElemental = GetHasFeat(FEAT_AIR_DOMAIN_POWER) + GetHasFeat(FEAT_EARTH_DOMAIN_POWER) + GetHasFeat(FEAT_FIRE_DOMAIN_POWER) + GetHasFeat(FEAT_WATER_DOMAIN_POWER); + int nVermin = GetHasFeat(FEAT_PLANT_DOMAIN_POWER) + GetHasFeat(FEAT_ANIMAL_COMPANION); + int nConstructs = GetHasFeat(FEAT_DESTRUCTION_DOMAIN_POWER); + int nPlanarTurning = GetHasFeat(AI_FEAT_EPIC_PLANAR_TURNING); + // Outsiders can be turned via. Epic Planar Turning too. + int nOutsider = GetHasFeat(FEAT_GOOD_DOMAIN_POWER) + GetHasFeat(FEAT_EVIL_DOMAIN_POWER) + nPlanarTurning; + int nHD, nRacial, iValid; + // Loop, using a sort of loop used for the turning check. + // - Add GetObjectSeen/GetObjectHeard. + // - We stop if, when we would use it, it'd re-apply effects to one + // already turned. + // - We stop if we get to something we can turn! + int nCnt = i1; + object oTarget = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC , OBJECT_SELF, nCnt); + while(GetIsObjectValid(oTarget) && iValid == FALSE && GetDistanceToObject(oTarget) <= f20) + { + if(!GetIsFriend(oTarget) && (GetObjectSeen(oTarget) || GetObjectHeard(oTarget))) + { + nHD = GetHitDice(oTarget) + GetTurnResistanceHD(oTarget); + nRacial = GetRacialType(oTarget); + // Planar creatures add spell resistance (don't bother if can't turn them) + if(nRacial == RACIAL_TYPE_OUTSIDER && nOutsider) + { + if(nPlanarTurning) + { + // Half with the epic feat + nHD += (GetSpellResistance(oTarget)/i2); + } + else + { + // Full SR added without the epic feat. + nHD += GetSpellResistance(oTarget); + } + } + if(nHD <= nTurnLevel) + { + // If we would try and re-apply effects, then break and do + // not use + if(GetHasSpellEffect(AI_SPELL_FEAT_TURN_UNDEAD, oTarget) || + GetHasSpellEffect(AI_SPELL_FEAT_PLANAR_TURNING, oTarget)) + { + iValid = i2; + } + // Else Check the various domain turning types + else if(nRacial == RACIAL_TYPE_UNDEAD || + (nRacial == RACIAL_TYPE_VERMIN && nVermin) || + (nRacial == RACIAL_TYPE_ELEMENTAL && nElemental) || + (nRacial == RACIAL_TYPE_CONSTRUCT && nConstructs) || + (nRacial == RACIAL_TYPE_OUTSIDER && nOutsider)) + { + iValid = TRUE; + } + } + } + nCnt++; + oTarget = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, OBJECT_SELF, nCnt); + } + // If valid, use it + if(iValid == TRUE) + { + // 28: "[DCR:Turning] Using Turn Undead" + DebugActionSpeakByInt(28); + // We do not bother turning off hiding/searching - but we do check + // expertise! + if(GlobalIntelligence >= i4) + { + if(GetHasFeat(FEAT_IMPROVED_EXPERTISE)) + { + AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE); + } + else if(GetHasFeat(FEAT_EXPERTISE)) + { + AI_SetMeleeMode(ACTION_MODE_EXPERTISE); + } + } + ActionUseFeat(FEAT_TURN_UNDEAD, OBJECT_SELF); + return TRUE; + } + } + // Else nothing to do it against or no feat. + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name TalentBardSong +//:://///////////////////////////////////////////// + This, if we have the feat, and not the effects, + will use it on ourselves. +//:://///////////////////////////////////////////// +//:: Created By: Bioware +//::////////////////////////////////////////////*/ +int AI_AttemptFeatBardSong() +{ + // Got it and not silenced + if(GetHasFeat(FEAT_BARD_SONGS) && !AI_GetAIHaveEffect(GlobalEffectSilenced)) + { + // the spell script used is 411 + // Get nearest without this spell's effect to check too + object oSonged = GetNearestCreature(CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, AI_SPELL_BARD_SONG, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + if((!GetHasSpellEffect(AI_SPELL_BARD_SONG) && + !GetHasFeatEffect(FEAT_BARD_SONGS) && + // Do not use if we are deaf... + !AI_GetAIHaveEffect(GlobalEffectDeaf, oSonged)) || + // ...but we could use it if we have an undeaf ally in 5M + (GetIsObjectValid(oSonged) && GetDistanceToObject(oSonged) < f5 && + !AI_GetAIHaveEffect(GlobalEffectDeaf, oSonged))) + { + // 29: "[DCR:Bard Song] Using" + DebugActionSpeakByInt(29); + // We do not bother turning off hiding/searching + ActionUseFeat(FEAT_BARD_SONGS, OBJECT_SELF); + return TRUE; + } + // We may use curse song! + else if(GetHasFeat(FEAT_CURSE_SONG)) + { + // get nearest without this spell's effect + oSonged = GetNearestCreature(CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, AI_SPELL_CURSE_SONG, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + // If they are not cursed, IE valid, in 6M, and not deaf, we try it. + if(GetIsObjectValid(oSonged) && + GetDistanceToObject(oSonged) <= f6 && + !AI_GetAIHaveEffect(GlobalEffectDeaf, oSonged)) + { + // 30: "[DCR:Bard Curse Song] Using" + DebugActionSpeakByInt(30); + // We do not bother turning off hiding/searching + ActionUseFeat(FEAT_CURSE_SONG, OBJECT_SELF); + return TRUE; + } + } + } + return FALSE; +} + +// Wrappers Greater and Lesser spell breach. +int AI_ActionCastBreach() +{ + // Greater Spell Breach. Level 6 (Wizard). Dispels cirtain protections automatically + lowers spell resistance + if(AI_ActionCastSpell(SPELL_GREATER_SPELL_BREACH, SpellHostRanged, GlobalDispelTarget, i16, FALSE, ItemHostRanged)) return TRUE; + // Lesser Spell Breach. Level 4 (Wizard) Dispels cirtain protections automatically + lowers spell resistance + if(AI_ActionCastSpell(SPELL_LESSER_SPELL_BREACH, SpellHostRanged, GlobalDispelTarget, i14, FALSE, ItemHostRanged)) return TRUE; + return FALSE; +} +// Wrappers all dispel spells +int AI_ActionCastDispel() +{ + // Mordenkainens Disjunction. Level 9 (Wizard). Acts like a breach, dispel AND lowers SR, in a big area. + if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, GlobalDispelTarget, i19, FALSE, ItemHostAreaInd)) return TRUE; + // Greater Dispeling. Level 5 (Bard/Innate) 6 (Cleric/Druid/Mage) General dispel up to 15 caster levels. + if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, GlobalDispelTarget, i16, FALSE, ItemHostRanged)) return TRUE; + // Dispel Magic. Level 3 (Bard/Cleric/Paladin/Mage/Innate) 6 (Druid) General dispel up to 10 caster levels. + if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, GlobalDispelTarget, i13, FALSE, ItemHostRanged)) return TRUE; + // Only lesser if under 15 HD, because it may not be worth it (DC wise) + if(GlobalAverageEnemyHD < i15) + { + // Lesser Dispel. Level 1 (Bard) 2 (Cleric/Druid/Mage/Innate). General dispel up to 5 caster levels. + if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, GlobalDispelTarget, i12, FALSE, ItemHostAreaInd)) return TRUE; + } + return FALSE; +} +// Wrappers Premonition, Greater Stoneskin and Stoneskin. +// Includes Shades Stoneskin too. SPELL_SHADES_STONESKIN +// - iLowest - 8 = Prem, 6 = Greater, 4 = Stoneskin +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) +int AI_SpellWrapperPhisicalProtections(int iLowest = 1) +{ + // Stoneskin - protection from attackers. + if(!AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections)) + { + // Epic Warding (Mage Only) - Damage reduction 50/+20. Lasts 1 round per level. + if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_EPIC_WARDING, AI_SPELL_EPIC_WARDING)) return TRUE; + + // Psionic Inertial Barrier + if(AI_ActionCastSpell(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER, SpellProSelf)) return TRUE; + + if(iLowest <= i8) + { + // Preminition is 30/+5 damage resistance. Level 8 spell. Always class + // as level 9. (Mage) + if(AI_ActionCastSpell(SPELL_PREMONITION, SpellProSelf, OBJECT_SELF, i18, FALSE, ItemProSelf, PotionPro)) return TRUE; + if(iLowest <= i6) + { + // Then, greater stoneskin protects a lot of damage - 20/+5. + // We also consider this to be a high-level spell. Level 6 (Mage) 7 (druid) + if(AI_ActionCastSpell(SPELL_GREATER_STONESKIN, SpellProSelf, OBJECT_SELF, i16, FALSE, ItemProSelf, PotionPro)) return TRUE; + if(iLowest <= i4) + { + // Shades stoneskin. SPELL_SHADES_STONESKIN + if(AI_ActionCastSubSpell(SPELL_SHADES_STONESKIN, SpellProSinTar, OBJECT_SELF, i16, FALSE, ItemProSinTar)) return TRUE; + + // Stoneskin is next, level 4, but 10/+5 resistance Level 4 (Mage) 5 (Druid) + if(AI_ActionCastSpell(SPELL_STONESKIN, SpellProSinTar, OBJECT_SELF, i14, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + } + } + } + return FALSE; +} +// Wrappers Energy Buffer, Protection From Elements, Resist Elements, Endure elements +// - iLowest - Goes 5, 3, 2, 1. +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) +int AI_SpellWrapperElementalProtections(int iLowest = 1) +{ + // Elemental protections (fireball ETC) + if(!AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) && iLowest <= i5) + { + // Energy buffer - 40/- resistance to the damage. + // 6 for druids ETC, but 5 Mage/Innate. + if(AI_ActionCastSpell(SPELL_ENERGY_BUFFER, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar, PotionPro)) return TRUE; + if(iLowest <= i3) + { + // Protection from elements - 30/- resistance to the damage. Level 3 + if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_ELEMENTS, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE; + if(iLowest <= i2) + { + // Resist elements - 20/- resistance to the damage. Level 2 + if(AI_ActionCastSpell(SPELL_RESIST_ELEMENTS, SpellProSinTar, OBJECT_SELF, i12, FALSE, ItemProSinTar, PotionPro)) return TRUE; + if(iLowest <= i1) + { + // Endure elements - 10/- resistance to the damage. Level 1 + if(AI_ActionCastSpell(SPELL_ENDURE_ELEMENTS, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + } + } + } + return FALSE; +} +// Wrappers Haste and Mass Haste. +// - iLowest - 6 = Mass, 3 = Haste +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) +int AI_SpellWrapperHasteEnchantments(int iLowest = 1) +{ + // Excelent to cast this normally - +4AC. Probably normally best after protections from + // phisical attacks. + if(!AI_GetAIHaveEffect(GlobalEffectHaste) && iLowest <= i6) + { + // The feat, for haste, called blinding speed. + if(AI_ActionUseFeatOnObject(FEAT_EPIC_BLINDING_SPEED)) return TRUE; + + // Mass haste, level 6 spell. (Mage/Bard) Affects allies, and adds +1 action, +4 AC and +150% move + if(AI_ActionCastSpell(SPELL_MASS_HASTE, SpellEnhAre, OBJECT_SELF, i16, FALSE, ItemEnhAre, PotionEnh)) return TRUE; + if(iLowest <= i3) + { + // Haste, Level 3 spell. (Mage/Bard) + if(AI_ActionCastSpell(SPELL_HASTE, SpellEnhSinTar, OBJECT_SELF, i13, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + } + } + return FALSE; +} +// Wrappers Shadow Shield, Ethereal Visage and Ghostly Visage. +// Includes Greater Shadow Conjuration Ghostly Visage (SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE +// - iLowest - 7 = Shadow, 6 = Ethereal 2 = Ghostly +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections) +int AI_SpellWrapperVisageProtections(int iLowest = 1) +{ + // Visages are generally lower DR, with some spell-resisting or effect-immunty extras. + if(!AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections) && iLowest <= i7) + { + // Shadow Shield - has 10/+3 damage reduction + // Level 7 (Mage) + if(AI_ActionCastSpell(SPELL_SHADOW_SHIELD, SpellProSelf, OBJECT_SELF, i17, FALSE, ItemProSelf, PotionPro)) return TRUE; + if(iLowest <= i6) + { + // Ethereal Visage (level 6 (Mage)) is 20/+3 damage reduction - no limit! + // + Immunity to level 2, 1, 0 level spells. + if(AI_ActionCastSpell(SPELL_ETHEREAL_VISAGE, SpellEnhSelf, OBJECT_SELF, i16, FALSE, ItemEnhSelf, PotionEnh)) return TRUE; + if(iLowest <= i2) + { + // This is the shadow dancer evade + if(AI_ActionUseFeatOnObject(FEAT_SHADOW_EVADE)) return TRUE; + + // This is the assassin ghostly visage. + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_SPELL_GHOSTLY_VISAGE)) return TRUE; + + // This is ghostly visage, dispite the spell constant name. G.Shadow conjuration + if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar)) return TRUE; + + // Ghostly Visage is only 5/+1, but could work at the lower levels! Immunity to 1-0 spells too. + // Level 2 (Mage). + if(AI_ActionCastSpell(SPELL_GHOSTLY_VISAGE, SpellProSelf, OBJECT_SELF, i12, FALSE, ItemProSelf, PotionPro)) return TRUE; + } + } + } + return FALSE; +} +// Wrappers All Mantals (Greater, Normal, Lesser) (Spell Mantals) +// - iLowest - 9 = Greater, 7 = Normal. 5 = Lesser +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) +int AI_SpellWrapperMantalProtections(int iLowest = 1) +{ + // Provides total spell immunity (for most spells, say, 99%) until runs out/dispeled. + if(!AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) && iLowest <= i9) + { + // Greater Spell mantal. Level 9 (Mage), for d12() + 10 spell levels protected. + if(AI_ActionCastSpell(SPELL_GREATER_SPELL_MANTLE, SpellProSelf, OBJECT_SELF, i19, FALSE, ItemProSelf, PotionPro)) return TRUE; + if(iLowest <= i7) + { + // Normal, level 7 spell (Mage). d6() + 8 spell levels protected. + if(AI_ActionCastSpell(SPELL_SPELL_MANTLE, SpellProSelf, OBJECT_SELF, i17, FALSE, ItemProSelf, PotionPro)) return TRUE; + if(iLowest <= i5) + { + // Lesser, level 5 spell (Mage). d4() + 6 spell levels protected. + if(AI_ActionCastSpell(SPELL_LESSER_SPELL_MANTLE, SpellProSelf, OBJECT_SELF, i15, FALSE, ItemProSelf, PotionPro)) return TRUE; + } + } + } + return FALSE; +} +// Wrappers All Globes (Greater, Shadow Conjuration, Minor) +// - iLowest - 6 = Greater, 4 = Shadow/Minor +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasGlobeProtections) +int AI_SpellWrapperGlobeProtections(int iLowest = 1) +{ + // Immunity to most spells of X or under. Good because level 4 spells have a large selection of AOE ones. + if(!AI_GetAIHaveSpellsEffect(GlobalHasGlobeProtections) && iLowest <= i6) + { + // Normal globe, level 6 spell (Mage). Protects vurses 4 or lower spells. + if(AI_ActionCastSpell(SPELL_GLOBE_OF_INVULNERABILITY, SpellProSinTar, OBJECT_SELF, i16, FALSE, ItemProSinTar, PotionPro)) return TRUE; + // Note! Ethereal Visage protects VS 0, 1, 2 levels spells. + if(iLowest <= i4) + { + // SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE. + // As Minor globe, except shadow version + if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, SpellProSelf, OBJECT_SELF, i15, FALSE, ItemProSelf, PotionPro)) return TRUE; + + // Minor globe, level 4 spell (Mage). Protects vurses 3 or lower spells. + if(AI_ActionCastSpell(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, SpellProSelf, OBJECT_SELF, i14, FALSE, ItemProSelf, PotionPro)) return TRUE; + } + } + return FALSE; +} +// Wrappers All Shields - Elemental Shield, Wounding Whispers +// - iLowest - 4 = Elemental, 3 = Wounding. +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalShieldSpell) +int AI_SpellWrapperShieldProtections(int iLowest = 1) +{ + // These help deflect damage :-D + if(!AI_GetAIHaveEffect(GlobalEffectDamageShield) && iLowest <= i4) + { + // Elemental Shield. Level 4 (Mage). Does some damage to melee attackers (Casterlvl + d6(), Fire). +50% cold/fire resistance. + if(AI_ActionCastSpell(SPELL_ELEMENTAL_SHIELD, SpellProSinTar, OBJECT_SELF, i14, FALSE, ItemProSinTar, PotionPro)) return TRUE; + if(iLowest <= i3) + { + // Acid Sheath. Level 3 (Mage) Does some damage to melee attackers (2 * CasterLvl + 1d6, Acid). + if(AI_ActionCastSpell(SPELL_MESTILS_ACID_SHEATH, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE; + + // Wounding Whispers. Level 3 (bard) Does some damage to melee attackers (Casterlvl + d6(), Sonic). + if(AI_ActionCastSpell(SPELL_WOUNDING_WHISPERS, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE; + + if(iLowest <= i2) + { + // Death Armor. Level 2 (Mage) Does some damage to melee attackers (Caster Level/2 (to +5) + 1d4, Magical) + if(AI_ActionCastSpell(SPELL_DEATH_ARMOR, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + } + } + return FALSE; +} +// Wrappers All Mind resistance spells - Mind blank, Lesser and Clarity. bioware likes 3's... +// - iLowest - 8 = Mind Blank, 5 = Lesser, 2 = Clarity +// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMindResistanceProtections) +int AI_SpellWrapperMindResistanceProtections(int iLowest = 1) +{ + // immunties against mind - cool :-D (but not 100% useful). + // Might add more "if they cast a mind spell, we cast this to stop more" in sometime + if(!AI_GetAIHaveSpellsEffect(GlobalHasMindResistanceProtections) && iLowest <= i8) + { + // Mind Blank. Level 8 (Mage) Mind immunity (and clean up) + if(AI_ActionCastSpell(SPELL_MIND_BLANK, SpellEnhSinTar, OBJECT_SELF, i18, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + if(iLowest <= i5) + { + // Lesser Mind Blank. Level 5 (Mage) Mind immunity (and clean up) + if(AI_ActionCastSpell(SPELL_LESSER_MIND_BLANK, SpellEnhSinTar, OBJECT_SELF, i15, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + if(iLowest <= i2) + { + // Clarity. Level 2 (Bard/Innate) 3 (Cleric/Mage) Mind immunity (and clean up) + if(AI_ActionCastSpell(SPELL_CLARITY, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + } + } + } + return FALSE; +} + +// Wrappers All Consealment spells - Improved Invisiblity. Displacement. +// - iLowest - 4 = Improved Invisiblit, 3 = Displacement +// - Checks !AI_GetAIHaveEffect(GlobalEffectInvisible, oTarget) && !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells, oTarget) +int AI_SpellWrapperConsealmentEnhancements(object oTarget, int iLowest = 1) +{ + if(!AI_GetAIHaveEffect(GlobalEffectInvisible, oTarget) && + !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells, oTarget)) + { + // Shadow dragon special consealment + if(AI_ActionCastSpell(AI_SPELLABILITY_SHADOWBLEND)) return TRUE; + + if(iLowest <= i4) + { + // Improved Invis - assassin + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_INVISIBILITY_2)) return TRUE; + + // Improved Invis. Level 4 (Bard, Mage). 50% consealment + invisibility. + if(AI_ActionCastSpell(SPELL_IMPROVED_INVISIBILITY, SpellEnhSinTar, OBJECT_SELF, i14, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + + if(iLowest <= i3) + { + // Displacement. Level 3 (Mage). 50% consealment. + if(AI_ActionCastSpell(SPELL_DISPLACEMENT, SpellEnhSinTar, OBJECT_SELF, i13, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + } + } + } + return FALSE; +} +// Shades darkness, assassin feat, normal spell. +int AI_SpellWrapperDarknessSpells(object oTarget) +{ + // Special Assassin Version + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_DARKNESS, oTarget)) return TRUE; + // Shades one + if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_DARKNESS, SpellOtherSpell, oTarget, i14, TRUE)) return TRUE; + // Darkness. Area of consealment. Level 2 (Cleric/Bard/Mage) Specail Assassin. + if(AI_ActionCastSpell(SPELL_DARKNESS, SpellOtherSpell, oTarget, i12, TRUE)) return TRUE; + + return FALSE; +} +// Invisibility spells + feats +int AI_SpellWrapperNormalInvisibilitySpells() +{ + // Special Harper Version + if(AI_ActionUseFeatOnObject(FEAT_HARPER_INVISIBILITY)) return TRUE; + // Special Assassin Version + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_INVISIBILITY_1)) return TRUE; + // Cast invisiblity spells! + // Invisiblity Sphere. Level 3 (Bard, Mage). Invisibility till attacked for an area! + if(AI_ActionCastSpell(SPELL_INVISIBILITY_SPHERE, SpellEnhAre, OBJECT_SELF, i13, FALSE, ItemEnhAre)) return TRUE; + // Shad. conjuration + if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_INIVSIBILITY, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + // Invisiblity. Level 2 (Bard, Mage). Invisibility till attacked. + if(AI_ActionCastSpell(SPELL_INVISIBILITY, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + + return FALSE; +} + +// This will loop seen allies in a cirtain distance, and get the first one without +// the spells effects of iSpell1 to iSpell4 (and do not have the spells). +// - Quite expensive loop. Only used if we have the spell (iSpellToUse1+) +// in the first place (no items!) and not the timer which stops it for a few rounds (on iSpellToUse1) +// - TRUE if it casts its any of iSpellToUseX's. +// * It has only a % chance to cast if GlobalWeAreBuffer is not TRUE. +int AI_ActionCastAllyBuffSpell(float fMaxRange, int iPercent, int iSpellToUse1, int iSpellToUse2 = -1, int iSpellToUse3 = -1, int iSpellToUse4 = -1, int iSpellOther1 = -1, int iSpellOther2 = -1) +{ + // Not in time stop + if(GlobalInTimeStop) return FALSE; + + // Check buff ally is valid + if(!GetIsObjectValid(GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + s1))) return FALSE; + + // Check % (buffers add 150, so always pass this) + if(d100() > (iPercent + // Default % + (i150 * GlobalWeAreBuffer) - // Always cast if buffer + (GlobalWeAreSorcerorBard * i50) + // Much less (50%) if sorceror/bard + (FloatToInt(GlobalRangeToMeleeTarget))// Add a little for range to melee target + )) return FALSE; + + // Check local timer for the top spell to cast against the ally. Only the + // top spell is timed. + if(GetLocalTimer(AI_TIMER_BUFF_ALLY_SPELL + IntToString(iSpellToUse1))) return FALSE; + + // Set local timer to not use it for a while + SetLocalTimer(AI_TIMER_BUFF_ALLY_SPELL + IntToString(iSpellToUse1), f18); + + // Check if we have the spell + if(!GetHasSpell(iSpellToUse1) && !GetHasSpell(iSpellToUse2) && + !GetHasSpell(iSpellToUse3) && !GetHasSpell(iSpellToUse4)) return FALSE; + + // - This lets real-hardcode buffers go to a longer range. + float fRangeToGoTo = fMaxRange + GlobalBuffRangeAddon; + // Loop nearest to futhest allies + int iCnt = i1; + object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + IntToString(iCnt)); + // Loop Targets + // - iCnt is our breaker. We only check 10 nearest allies, and set to 20 if break. + while(GetIsObjectValid(oAlly) && iCnt <= i10 && GetDistanceToObject(oAlly) <= fRangeToGoTo) + { + // Check for thier effects + if((iSpellToUse1 == iM1 || !GetHasSpellEffect(iSpellToUse1, oAlly)) && + (iSpellToUse2 == iM1 || !GetHasSpellEffect(iSpellToUse2, oAlly)) && + (iSpellToUse3 == iM1 || !GetHasSpellEffect(iSpellToUse3, oAlly)) && + (iSpellToUse4 == iM1 || !GetHasSpellEffect(iSpellToUse4, oAlly)) && + (iSpellOther1 == iM1 || !GetHasSpellEffect(iSpellOther1, oAlly)) && + (iSpellOther2 == iM1 || !GetHasSpellEffect(iSpellOther2, oAlly)) && + !GetHasSpell(iSpellToUse1, oAlly) && !GetHasSpell(iSpellToUse2, oAlly) && + !GetHasSpell(iSpellToUse3, oAlly) && !GetHasSpell(iSpellToUse4, oAlly)) + { + // Break with this ally as target + iCnt = i20; + } + else + { + // Get Next ally + iCnt++; + oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + IntToString(iCnt)); + } + } + // If valid, cast at the target and return TRUE. + if(iCnt == i20) + { + // oAlly is our buff target - cast the best to worst on it! + if(AI_ActionCastSpell(iSpellToUse1, FALSE, oAlly)) return TRUE; + if(AI_ActionCastSpell(iSpellToUse2, FALSE, oAlly)) return TRUE; + if(AI_ActionCastSpell(iSpellToUse3, FALSE, oAlly)) return TRUE; + if(AI_ActionCastSpell(iSpellToUse4, FALSE, oAlly)) return TRUE; + } + // Return FALSE - no spell cast + return FALSE; +} + +// This will run through most avalible protections spells. +// - TRUE if we cast any. +// Used when invisible to protect before we break the invisibility. +// - We may cast many on allies too +int AI_ActionCastWhenInvisible() +{ + // We run through some spells. Primarily, protection then buffs. + // We don't target others else we'd break the invisiblity. + + // Haste's + if(AI_SpellWrapperHasteEnchantments()) return TRUE; + + // Stoneskin - protection from attackers. + if(AI_SpellWrapperPhisicalProtections()) return TRUE; + + // Mantals + if(AI_SpellWrapperMantalProtections()) return TRUE; + + // Elemental protections (fireball ETC) + if(AI_SpellWrapperElementalProtections()) return TRUE; + + // Visages + if(AI_SpellWrapperVisageProtections()) return TRUE; + + // Globes + if(AI_SpellWrapperGlobeProtections()) return TRUE; + + // Shields + if(AI_SpellWrapperShieldProtections()) return TRUE; + + // Mind resistances + if(AI_SpellWrapperMindResistanceProtections()) return TRUE; + + // Thats us done, what about allies? + // - Buffs for allies (Reference!) + // Stoneskin (+ Greater) + // Improved Invisibility, Displacement + // Haste, Mass Haste + // Energy Protections (Buffer, Protection From, Resist, Endure) + // Ultravision (special case: Nearest seen ally with Darkness and not this effect) + // Spell Resistance + // Death Ward (If we can see arcane spellcasters, as it only provides death immunity) + // Regenerate, Monstourous Regeneration + // Negative Energy Protection (If we see any clerics, druids, with harm, else only self) + + // Only if buffer: + // Mage armor, Barkskin + // Bless, Aid + // Bulls Strength, Cats Grace, Endurance + // Stone bones - On undead only. + + // Not cast: + // Weapon spells (Blackstaff ETC) - We wouldn't have many. + // Protection From Spells - AOE, and the AOE includes us anyway (so gets captured when we cast it) + // Mind blanks - Mind protection, not worth casting on allies, its more for removal. + // Normal invisilbilties - They would normally be broken right away. + // Natures Balance - Healing AOE only really, mostly enemy SR lowering in AOE. + // Prayer - Cast on self + // Freedom of movement - More removal if anyone needs it. Doesn't stop too much. + + // Much lower % as sorceror or bard. + + // Try stoneskins as a main one + if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_GREATER_STONESKIN, SPELL_STONESKIN)) return TRUE; + + // Hastes! + if(AI_ActionCastAllyBuffSpell(f8, i100, SPELL_HASTE, SPELL_MASS_HASTE)) return TRUE; + + // Consealment spells + if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_IMPROVED_INVISIBILITY, SPELL_DISPLACEMENT)) return TRUE; + + // Elemental protections + if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_ENERGY_BUFFER, SPELL_PROTECTION_FROM_ELEMENTS, SPELL_RESIST_ELEMENTS, SPELL_ENDURE_ELEMENTS)) return TRUE; + + // If we have the setting to buff allies, we carry on buffing with some + // other spells. + if(GlobalWeAreBuffer) + { + // Some AC protections + if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_MAGE_ARMOR, SPELL_BARKSKIN)) return TRUE; + + // Bulls Strength, Cats Grace, Endurance + if(AI_ActionCastAllyBuffSpell(f10, i100, SPELL_ENDURANCE, SPELL_CATS_GRACE, SPELL_ENDURANCE, iM1, SPELL_GREATER_BULLS_STRENGTH, SPELL_GREATER_CATS_GRACE)) return TRUE; + + // Bless, Aid + if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_AID, SPELL_BLESS)) return TRUE; + } + // Return FALSE - nothing cast + return FALSE; +} + +// Using the array ARRAY_ENEMY_RANGE, we return a % of seen/heard people who +// DO have any of the spells which see through the invisiblity spells. +// * iLimit - when we get to this number of people who have invisiblity, we stop and return 100% +// If ANY of the people are attacking us and have the effect, we return +30% for each. +int AI_GetSpellWhoCanSeeInvis(int iLimit) +{ + // Loop LOS range enemies. + int iCnt = i1; + int iHasSeeingTotal, iTotal, iAdditional; + // Loop start + object oEnemy = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oEnemy) && iAdditional < i100) + { + // Seen/heard check is already done + // Add one to total. + iTotal++; + // Make this the total of any seeing spells. + if(GetHasSpellEffect(SPELL_SEE_INVISIBILITY, oEnemy) || + GetHasSpellEffect(SPELL_TRUE_SEEING, oEnemy) || + // - We obviously can tell with creatures with true seeing hides. Only checking hides! + GetItemHasItemProperty(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oEnemy), ITEM_PROPERTY_TRUE_SEEING)) + { + iHasSeeingTotal++; + // Limit checking + if(iHasSeeingTotal >= iLimit) + { + iAdditional += i100; + } + // Special: If they are attacking us (with it) we add 30% + // to outcome, and add 1. + else if(GetAttackTarget(oEnemy) == OBJECT_SELF) + { + iAdditional += i30; + } + } + iCnt++; + oEnemy = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + } + if(iHasSeeingTotal > FALSE) + { + return AI_GetPercentOf(iHasSeeingTotal, iTotal) + iAdditional; + } + return FALSE; +} + +// Returns a dismissal target - a target with a master, and they +// are a Familiar, Animal companion or summon. +// - Nearest one in 10 M. Seen ones only. +object AI_GetDismissalTarget() +{ + object oMaster, oReturn; + int iCnt = i1; + string sCnt = IntToString(iCnt); + object oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt); + float fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt); + while(GetIsObjectValid(oLoopTarget) && fDistance <= f10) + { + // Check if they are a valid summon/familar/comapnion + oMaster = GetMaster(oLoopTarget); + //Is that master valid and is he an enemy + if(GetIsObjectValid(oMaster) && GetIsEnemy(oMaster)) + { + //Is the creature a summoned associate + if(GetAssociate(ASSOCIATE_TYPE_SUMMONED, oMaster) == oLoopTarget || + GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oMaster) == oLoopTarget || + GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oMaster) == oLoopTarget) + { + // Stop and break + oReturn = oLoopTarget; + break; + } + } + // Get next target + iCnt++; + sCnt = IntToString(iCnt); + fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt); + oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt); + } + return oReturn; +} +/*:://///////////////////////////////////////////// +//:: Name ImportAllSpells +//:://///////////////////////////////////////////// + Taken from Jugulators improved spell AI (but now, hardly any of it remains!). + This is a very heavily changed version. If they can cast spells + or abilities, this will run though them and choose which + to cast. It will cast location spells at location, meaning heard + enemies may still be targeted. + + Incudes, nearer the bottom, if we are not a spellcaster, and + our BAB is high compared to thier AC, we will HTH attack + rather than carry on casting low level spells. + + Now: 1.3, it uses setups of what to do from OnSpawn to the best of its + abilities. It already has chosen an appropriate target (And decerned its + properties, immunities). + + Note: These are some of the non-verbal and non-stomatic componants: + + - Non-Verbal Spells - + Clarity, Lesser Dispel, and Ethereal Visage. + + - Non-Somatic Spells - + Darkness, Knock, Light, Mass Charm, Mordenkainen's Disjunction, Polymorph + Self, Power Word Kill, Power Word Stun, Wail of the Banshee, Word of Faith, + and War Cry. + + We ALWAYS use the nearest seen, if no one else :-P. + + Note: On Setup, like fighter choices, we normally perfere futher away targets + which we can see :-) as fighters go for nearer ones, but they must be the + best! + + This attempts to cast a spell, running down the lists. + The only variable is iLowest and iBAB level's, targets are globally set. + - iLowestSpellLevel - If it is set to more than 1, then all spells of that + level and lower are just ignored. Used for dragons. Default 0. + - iBABCheckHighestLevel - We check our BAB to see if attacking them would probably + be a better thing to do. Default 3, it adds 5DC for each level. + - iLastCheckedRange - 1 = Minimum, 4 = Longest. If it runs through once, and + with "ranged attacking", doesn't find any spells to cast at the "long" range, + then it will attempt to see if there are any spells for "meduim" etc. + - Range: Long 40, Medium: 20, Short: 10, Touch: 2.25 (Game meters). Personal = 0 + NOTE 1: If GlobalItemsOnly is set, we only check for item talents! + NOTE 2: It uses the SAME target, but for ranged attacking, we use a float range to check range :-) + + Notes on touch attacks: + [Quote] + Here's how the AC is added up for touch attacks: + + Armor bonus: no + Shield bonus: no + Deflection bonus: yes + Natural bonus: no + Dodge bonus: yes + Dexterity modifier: yes + Monk wisdom: yes +//:://///////////////////////////////////////////// +//:: Created By: Jugulator, Modified (very) Heavily: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptAllSpells(int iLowestSpellLevel = 0, int iBABCheckHighestLevel = 3, int iLastCheckedRange = 1, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE) +{ + // Checks for valid numbers, ETC. + if(AI_GetAIHaveEffect(GlobalEffectPolymorph) || + (GlobalSpellAbilityModifier < i10 && !GobalOtherItemsValid && !GobalPotionsValid) || + GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_SPELLS, AI_OTHER_MASTER) || + // Do we have any spells to cast? + (GlobalSilenceSoItems && !GobalOtherItemsValid && !GobalPotionsValid) || + (!SpellAnySpellValid && !GobalOtherItemsValid && !GobalPotionsValid) || + !GetIsObjectValid(GlobalSpellTarget) || GetIsDead(GlobalSpellTarget)) + { + // 31: "[DCR: All Spells] Error! No casting (No spells, items, target Etc)." + DebugActionSpeakByInt(31); + return FALSE; + } + // 11: "[DCR: All Spells] [Modifier|BaseDC|SRA] " + IntToString(iInput) + // Input: 100 * GlobalSpellAbilityModifier) + 10 * GlobalSpellBaseSaveDC + SRA + DebugActionSpeakByInt(32, OBJECT_INVALID, (i100 * GlobalSpellAbilityModifier) + (i10 * GlobalSpellBaseSaveDC) + (SRA)); + + // Sets up AOE target object to get. + object oAOE, oRandomSpellNotUsedAOE, oPurge; + // (oRandomSpellNotUsedAOE is set when we radomise a spell, and the spell + // isn't cast, but could be!) + + // Notes on targets: + // Uses 1. + // - Ranged attacking may IGNORE shorter ranged spells IF they are not in range. + + // - Range: Long 40, Medium: 20, Short: 10, Touch: 2.25 (Game meters). Personal = 0 + // Range valids: + int RangeLongValid, RangeMediumValid, RangeShortValid, RangeTouchValid, + iCnt, iBreak, /* Counter things */ + iPercentWhoSeeInvis, iNeedToBeBelow, SingleSpellsFirst, + SingleSpellOverride, MultiSpellOverride, IsFirstRunThrough; + // Range LONG should ALWAYS be used, IE we can't get a longer ranged spell than that! + // SRA! + + if(!SRA) + { + RangeLongValid = TRUE; + RangeMediumValid = TRUE; + RangeShortValid = TRUE; + RangeTouchValid = TRUE; + } + // We stop if all set to TRUE, IE, already checked. + else if(InputRangeLongValid && InputRangeMediumValid && + InputRangeShortValid && InputRangeTouchValid) + { + // All done, stop + return FALSE; + } + // Ranges... + // If set to TRUE, it is ignored. + // If set to FALSE, we check the pass number or the range, and set to true. + else + { + // Long + if(InputRangeLongValid == i0) + { + // No range check, RangeLongValid is of course always true. + RangeLongValid = TRUE; + } + // At 1, we do not do anything, and ignore long range spells. + // Medium + if(InputRangeMediumValid == i0) + { + // Range check for medium - or if the run is at number 2+ + if(GlobalSpellTargetRange <= fMediumRange || + iLastCheckedRange >= i2) + { + RangeMediumValid = TRUE; + } + } + // At 1, we ignore medium + // Short + if(InputRangeShortValid == i0) + { + // Range check for short - or if it is >= pass 3 + if(GlobalSpellTargetRange <= fShortRange || + iLastCheckedRange >= i3) + { + RangeShortValid = TRUE; + } + } + // At 1, we ignore short ranged spells + // Touch + if(InputRangeTouchValid == i0) + { + // Range check for touch - or >= pass 4 + if(GlobalSpellTargetRange <= fTouchRange || + iLastCheckedRange >= i4) + { + RangeTouchValid = TRUE; + } + } + // At 1, we ignore touch ranged spells + } + + // IsFirstRunThrough is TRUE if this is the first run through, if + // last checked range is == 1 + // - Used, if TRUE, for defensive spells to stop checking the same things + // up to 4 times! + if(iLastCheckedRange == i1) + { + IsFirstRunThrough = TRUE; + } + + // these force the use of AOE spells, or single spells, over the other. + SingleSpellOverride = GetSpawnInCondition(AI_FLAG_COMBAT_SINGLE_TARGETING, AI_COMBAT_MASTER); + MultiSpellOverride = GetSpawnInCondition(AI_FLAG_COMBAT_MANY_TARGETING, AI_COMBAT_MASTER); + + // Saves. Stops them firing spells they would ALWAYs save against. + // GlobalSpellTargetWill, GobalSpellTargetFort, GlobalSpellTargetReflex. + + // SingleSpellsFirst = if TRUE, we try our single spells before the + // AOE spells. If false, don't bother. Not set if not seen target. + if(GlobalSeenSpell && (GlobalTotalSeenHeardEnemies < (GlobalOurHitDice / i3) || + GlobalTotalSeenHeardEnemies < i2 || + // Single targeting override. + SingleSpellOverride)) + { + SingleSpellsFirst = TRUE; + } + + // There is also the case of people immune to normal spells (such as + // Fireball) but not spells like Gaze of Death. + // - Set to the level of silence we cannot use, BTW. + // - Set to 10 if we have 90% or more spell failure. + // - Also includes any globes, ETC. + // - We use "if(GlobalNormalSpellsNoEffectLevel < 9)" for level 9 spells. + //GlobalNormalSpellsNoEffectLevel = 1-10. 10 being absolutely nothing. + // - EG: Set to 3, means 0, 1, 2, 3 have 0% chance of affecting them. + +/*::9999999999999999999999999999999999999999999999999999999999999999999999999999 +//:: Level 9 spells. +//::9999999999999999999999999999999999999999999999999999999999999999999999999999 + NOTE ABOUT HOW I DO SPELL LEVELS OF EACH SPELL! + Because there are spells for several classes, the same spell, such as Wall + of Fire, I take the MAGE (Sorceror/Wizard) class. This is because it will + then vary the sorcerors spells he casts. I then, if it is not a Mage spell, + generally take the lowest spell number (EG: Death Ward, level 4 cleric/bard, + 5 druid, and I'l take it as a level 4). This does not mean it will be cast + in the bracket for spell level X - especially if it is not a hostile spell. + + H = Hordes only (And I think includes SoU spells) + S = SoU only + + Protections are important, and are cast sometimes many levels above other + spells of thier level, like casting Stoneskin before Wail of the Banshee. + + Thoughts -level 9 spells are the powerhouses, or so they say. They are actually + not...good. Most are death save based, and so many higher-level PC's will + be immune with there being Death Ward handy/Shadow shield. Some good spells + however - energy drain is powerful and has maximum save DC, even if it is + necromantic. Storm of Vengance is a really powerful AOE persistant spell, + and the damaging spells are not half bad. Also, powerful summons at level 9! + + Epic: +H [Epic Mage Armor] - +5 of the 4 different AC types. Just +5 dodge is a great asset. +H [Hellball] - Long range AOE - 10d6 sonic, acid, fire and lightning damage to all in area. Reflex only halves. +H [Ruin] - 35d6 divine damage, fort save for half. +H [Mummy Dust] - Powerful summon that cannot be dispelled. 24 Hours. +H [Dragon Knight] - Powerful dragon summon that cannot be dispelled. 20 rounds. +H [Epic Warding] - Damage reduction 50/+20. Lasts 1 round per level. + + AOE: + [Wail Of the Banshee] - Save VS death (fort) or die. Doesn't affect caster. 10M Range. + [Wierd] - Save VS Will & Fort. Doesn't affect allies. 10M AOE. + [Meteor Swarm] - Non-friends in 10M are done with 20d6 damage. Reflex Save. + [Storm of Vengance] - Anyone in the AOE (10M where cast) gets reflex-electic, and alway-acid damage. + [Implosion] - +3 Save DC, VS death, medium AOE. Not affect self (but affects anyone else) + + [Modenkainens Disjunction] - Powerful Dispel. VERY powerful - acts like a breach as well! + + Single: + [Dominate Monster] - Save VS Will else dominated - anything! (3 turns +1 per caster level.) + [Energy Drain] - Save VS Fort or negative levels - 2d4. If it goes to 0 levels, kills. Also lots of negative stats! (Supernatural Effect) +S [Bigby's Crushing Hand] - (2d6 + 12 damage/round) + + Defender: + [Greater Spell Mantal] - Stops spells. d12 + 10 + [Time Stop] - 9 Seconds (Default amount anyway) that we stop everyone but ourselves. +S [Undeath's Eternal Foe] Stops negative damage, ability score draining, negative levels, immunity poisons, immunity diseases. + + Summon: + [Gate] - Balor. Short duration, powerful, (has spells) but need prot. From evil. + [Elemental Swarm] - Great! After one powerful one dies, another is summoned. Greater Elements + [Summon Creature 9] - Summons a random Elder Elemental. Normally 24HRS duration. + [Innate Summons] - Almost all innate ones are so easy to cast (no concentration) we cast them here. + - Summon Celestial (One Will'o'whisp) + - Summon Mephit (One Mephit) + - Summon Slaad (One red slaad) + - Summon Tanarri (One subbucus) +H - Summon Baatezu (One Erinyes) (Hordes) +H [Black Blade of Disaster] - A powerful greatsword fights. Please note: AI abuses the "concentration" rules for it! + + Other: + + These will be cast all the time :-) [Dismissal] is included - a special case + for any dismissal targets, of course. + + Same with [Haste]/[Mass Haste]. These are almost too good to miss! + +S [Etherealness] is a VERY powerful spell - if we cast this, we can normally + cast defensive spells while invisible - cool :-) + - We cast quite a few invisible-based spells near the top of our list. + + [Time stop] is a special case - we cast it first if we have 2 or more, cast + haste, then we are able to cast it again and get the maximum, safe, usage + out of it. + +H [Crumble] - Constructs only. + + Also note that [Stoneskin] (and variants) are usually cast at an upmost prioritory, + as mages have bad armor, saves and HP :-) + + Creatures with very powerful enchantments to dispel are dispeled, if they have + level 5 defenses. + + Harm/Heal are also cast here, to knock out enemies early on (if in range, + of course) + + Do Prismatic AC bonus adding here +//::99999999999999999999999999999999999999999999999999999999999999999999999999*/ + + // No BAB check. + + // Not in time stop + if(IsFirstRunThrough && !GlobalInTimeStop && GlobalIntelligence >= (i6 - d4())) + { + // We do this once, mind you, when we start at iLastCheckedRange == 4. + // Get a nearby enemy summon - 10M range. + oAOE = AI_GetDismissalTarget(); + // Is it valid? + if(GetIsObjectValid(oAOE)) + { + // Banishment. Level 6 (Cleric/Innate) 7 (Mage). Destroys outsiders as well as all summons in area around caster (10M) + if(AI_ActionCastSpell(SPELL_BANISHMENT, SpellHostAreaInd, OBJECT_SELF, i17, TRUE, ItemHostAreaInd)) return TRUE; + + // Dismissal is short range anyway. Enemy must be within 5M to be targeted. + // Dismissal. Level 4 (Bard/Cleric/Innate) 5 (Mage). At a will save (VS DC + 6) destroy summons/familiars/companions in area. + if(AI_ActionCastSpell(SPELL_DISMISSAL, SpellHostAreaDis, oAOE, i15, TRUE, ItemHostAreaDis)) return TRUE; + } + } + + // Time Stop - Never casts again in a timestop + // This will cast it very first if we have 2 or more (Sorceror) + if(IsFirstRunThrough && !GlobalInTimeStop && GlobalIntelligence >= i10 && + GetHasSpell(SPELL_TIME_STOP) >= i2) + { + // Time Stop. Level 9 (Mage). 9 Seconds (by default. Meant to be 1d4 + 1) of frozen time for caster. Meant to be a rare spell. + if(AI_ActionCastSpell(SPELL_TIME_STOP, SpellHostAreaDis, OBJECT_SELF, i19, FALSE, ItemHostAreaDis)) return TRUE; + } + + // Special case - Lots of phisical damage requires maximum protectoin. + // For this, we may even consider visage's to be worth something over stoneskin + // if we have no stoneskins :-D + // - We cast this if we have many melee attackers or total attackers. + // - To save time stop checking, we don't do this in time stop. + if(GlobalIntelligence >= i7 && !GlobalInTimeStop && IsFirstRunThrough && + !AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) && + !AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections) && + // Formula - IE They do 40 damage, we must be level 10 or less. :-) + // - As this is mainly for mages, we don't bother about how much HP left. + (GetAIInteger(AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT) >= GlobalOurHitDice * i4 || + // - Do we have anyone in 20M? + (GlobalRangeToNearestEnemy <= f20 && + // BAB check as well + GlobalAverageEnemyBAB >= GlobalOurHitDice / i2))) + { + // Stoneskins and so forth first, then visages. + + // We think that Stoneskin (which has /+5) is better then 15/+3 + if(AI_SpellWrapperPhisicalProtections(iLowestSpellLevel)) return TRUE; + + // Visage - lowest of Ethereal (6) however. Ghostly? Don't bother + if(iLowestSpellLevel <= i6) + { + if(AI_SpellWrapperVisageProtections(i6)) return TRUE; + } + } + // END phisical protections override check + + // Haste - First. Good one, I suppose. Should check for close (non-hasted) + // allies, to choose between them. + if(IsFirstRunThrough && !AI_GetAIHaveEffect(GlobalEffectHaste) && + !AI_CompareTimeStopStored(SPELL_HASTE, SPELL_MASS_HASTE)) + { + // I don't want to bother checking for allies for MASS_HASTE because + // mass haste is a good duration/harder to Dispel compared to haste + // anyway. + // - Should this be moved down? + if(AI_SpellWrapperHasteEnchantments(iLowestSpellLevel)) return TRUE; + } + + // Now the normal time stop. Power to the lords of time! (sorry, a bit over the top!) + // - Top prioritory. If you don't want it to cast it first, don't give it to them! + if(IsFirstRunThrough && !GlobalInTimeStop) + { + // Time Stop. Level 9 (Mage). 9 Seconds (by default. Meant to be 1d4 + 1) of frozen time for caster. Meant to be a rare spell. + if(AI_ActionCastSpell(SPELL_TIME_STOP, SpellHostAreaDis, OBJECT_SELF, i19, FALSE, ItemHostAreaDis)) return TRUE; + } + + // Special case - if lots of damage has been done elemetally wise, we will + // cast elemental protections (if not cast already). + // - To save time stop checking, we don't do this in time stop. + // - Only done if 4 or more intelligence. + if(SpellProSinTar /*Extra check here.*/ && + !AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) && + !GlobalInTimeStop && IsFirstRunThrough && GlobalIntelligence >= i4 && + // Formula - IE They do 40 damage, we must be level 10 or less. :-) + // - As this is mainly for mages, we don't bother about how much HP left. + (GetAIInteger(MAX_ELEMENTAL_DAMAGE) >= GlobalOurHitDice * i4 || + // OR last hit was major compared to total HP + GetAIInteger(LAST_ELEMENTAL_DAMAGE) >= GlobalOurMaxHP/i4)) + { + if(AI_SpellWrapperElementalProtections(iLowestSpellLevel)) + { + // Reset and return, if we cast something! + DeleteAIInteger(MAX_ELEMENTAL_DAMAGE); + return TRUE; + } + } + // END special elemental override check + + // Epic mage armor after specials. Not the best place... + // +20 AC is good, normally. + if(IsFirstRunThrough && !AI_GetAIHaveSpellsEffect(GlobalHasOtherACSpell)) + { + // Epic Mage Armor. (Mage only) +5 of the 4 different AC types. Just +5 dodge is a great asset. + if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_EPIC_MAGE_ARMOR, SPELL_EPIC_MAGE_ARMOR)) return TRUE; + } + + // Visibility Protections - going invisible! + // - We only do this not in time stop + // - We must be invisible + // - We must be of a decnt intelligence (5+) + // - We must make sure we don't already have all the protections listed + // - We MUST have not run through this once already. + if(!GlobalInTimeStop && GlobalIntelligence >= i5 && IsFirstRunThrough) + { + // First, check if we already have any effects. + // - If we have GlobalEffectEthereal, then we cast all (non-see through) + if(AI_GetAIHaveEffect(GlobalEffectEthereal) || + // - If we have darkness, we'll protect ourselves. (Ultravision or not!) + // * We can ultra vision in the override special actions part of the AI. + AI_GetAIHaveEffect(GlobalEffectDarkness)) + { + // Do some protection spells. :-) + // - And on allies! + if(AI_ActionCastWhenInvisible()) return TRUE; + } + // - If we have GlobalEffectInvisible, then we check who can see us. + // * If we have the timer Do not hide, and we didn't hide last + // turn, then we don't do it. + else if(AI_GetAIHaveEffect(GlobalEffectInvisible)) + { + if(!GetObjectSeen(OBJECT_SELF, GlobalMeleeTarget) || + GetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY)) + { + // Do some protection spells. :-) + // - And on allies! + if(AI_ActionCastWhenInvisible()) return TRUE; + } + } + // Else, no invisibility, so we may well cast it >:-D + // - Only cast it if we are not a sorceror or bard, or we have not got + // cirtain protections (important ones!) + // - Cast if any class other then bard/sorceror because we get advantages + // to use other spells and attacks. + else if(!GlobalWeAreSorcerorBard || + !AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) || + !AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) || + !AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) || + GlobalOurPercentHP >= i100) + { + // Is it a good time? (EG we want to flee, or want more protections :-) ). + // - Do we HAVE THE SPELLS?! + // - Are we overwhelmed? + // - Is the enemy a lot stronger? + // * Fleeing that uses this does it in the flee section (not in yet) + // * More protections for concentation is done in that section (not in yet) + + // 1. Etherealness. + // This is easily the best one there is! always works 100%%%! + // Etherealness. Total invisibility! Level 6 (Cleric) 8 (Mage) 7 (Innate). + if(AI_ActionCastSpell(SPELL_ETHEREALNESS, SpellOtherSpell, OBJECT_SELF, i17)) return TRUE; + + // 2. Darkness + // We cast this hopefully most of the time. We will cast it a lot if we have + // many melee attackers, else we'll cast it if we have ultravision/trueseeing + if(GlobalMeleeAttackers >= GlobalOurHitDice / i3 || + AI_GetAIHaveEffect(GlobalEffectUltravision) || + AI_GetAIHaveEffect(GlobalEffectTrueSeeing) || + GetHasSpell(SPELL_DARKVISION) || GetHasSpell(SPELL_TRUE_SEEING)) + { + // Darkness's + AI_SpellWrapperDarknessSpells(OBJECT_SELF); + } + // 3. Normal invisiblity. + // We need to make sure it won't be Dispeled by invis purge. + oPurge = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + OBJECT_SELF, i1, + CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, + CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_INVISIBILITY_PURGE); + if(!GetIsObjectValid(oPurge) || GetDistanceToObject(oPurge) > f10) + { + // Also, we can't go invisible if any enemies who are attacking us, + // can see us. We also won't if over 50 (or whatever)% of the enemy have got + // seeing spells. + // What % though? MORE if we have limited, selected spell (non-sorceror/bard) + // LESS if we do have other spells to take its place. + iPercentWhoSeeInvis = AI_GetSpellWhoCanSeeInvis(i4); + + // Here, we make sure it is less then 80% for mages, and less then + // 30% for sorcerors or bards. + iNeedToBeBelow = i80; + // Use global + if(GlobalWeAreSorcerorBard) iNeedToBeBelow = i30; + + if(iPercentWhoSeeInvis <= iNeedToBeBelow + d20()) + { + // If within d20 of the needed amount, we do cast improved + // invisibility. + // Special Assassin Version + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_INVISIBILITY_2)) + { + SetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY, f12); + return TRUE; + } + // Improved Invis. Level 4 (Bard, Mage). 50% consealment + invisibility. + if(AI_ActionCastSpell(SPELL_IMPROVED_INVISIBILITY, SpellEnhSinTar, OBJECT_SELF, i14, FALSE, ItemEnhSinTar, PotionEnh)) + { + SetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY, f12); + return TRUE; + } + // Other invisibilities. + if(iPercentWhoSeeInvis <= iNeedToBeBelow) + { + // Invisibility + if(AI_SpellWrapperNormalInvisibilitySpells()) + { + SetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY, f12); + return TRUE; + } + } + } + } + } + } + + // Massive summoning spells + // Normally, no...always, these are only owned by intelligent creatures, no + // need to random cast or anything. + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i12) + { + // Dragon Knight - Powerful dragon summon that cannot be dispelled. 20 rounds. + if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_DRAGON_KNIGHT, SPELL_EPIC_DRAGON_KNIGHT)) + { + // Set level to 12 + SetAIInteger(AI_LAST_SUMMONED_LEVEL, i12); + return TRUE; + } + } + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i11) + { + // Mummy Dust - Powerful summon that cannot be dispelled. 24 Hours. + if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_MUMMY_DUST, SPELL_EPIC_MUMMY_DUST)) + { + // Set level to 11 + SetAIInteger(AI_LAST_SUMMONED_LEVEL, i11); + return TRUE; + } + } + // This is POWERFUL! + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i10) + { + // Black Blade of Disaster. Level 9 (Mage) A powerful greatsword fights. Please note: AI abuses the "concentration" rules for it! + if(AI_ActionCastSummonSpell(SPELL_BLACK_BLADE_OF_DISASTER, i19, i10)) return TRUE; + } + + // If we are in time stop, or no enemy in 4 m, we will buff our appropriate stat. + // Level 2 spells. GLOBALINTELLIGENCE >= 6 + // NOte: CHANGE TO RANDOM ROLL, MORE CHANCE AT INT 10 + // Put just above first hostile spells. + // - Always cast if we have stoneskin effects. + if((GlobalInTimeStop || GlobalRangeToNearestEnemy > f4 || + AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections)) && + !GlobalIntelligence >= i6 && IsFirstRunThrough && + !AI_CompareTimeStopStored(SPELL_FOXS_CUNNING, SPELL_OWLS_WISDOM, + SPELL_EAGLE_SPLEDOR)) + { + if(GlobalOurChosenClass == CLASS_TYPE_WIZARD) + { + if(!AI_GetAIHaveSpellsEffect(GlobalHasFoxesCunningSpell) && + !AI_CompareTimeStopStored(SPELL_FOXS_CUNNING, SPELL_GREATER_FOXS_CUNNING)) + { + // Greater first. This provides +2d4 + 1. + // foxes cunning :-) - No items, innate. + if(AI_ActionCastSpell(SPELL_GREATER_FOXS_CUNNING, SpellEnhSinTar)) return TRUE; + // Lesser one - but we have items for it. + if(AI_ActionCastSpell(SPELL_FOXS_CUNNING, SpellEnhSinTar, OBJECT_SELF, i12, ItemEnhSinTar, PotionEnh)) return TRUE; + } + } + else if(GlobalOurChosenClass == CLASS_TYPE_DRUID || GlobalOurChosenClass == CLASS_TYPE_CLERIC) + { + if(!AI_GetAIHaveSpellsEffect(GlobalHasOwlsWisdomSpell) && + !AI_CompareTimeStopStored(AI_SPELL_OWLS_INSIGHT, SPELL_GREATER_OWLS_WISDOM, SPELL_OWLS_WISDOM)) + { + // Owls insight is cool - 2x caster level in wisdom = +plenty of DC. + // Owls Insight. Level 5 (Druid). + if(AI_ActionCastSpell(AI_SPELL_OWLS_INSIGHT, SpellEnhSinTar, OBJECT_SELF, i15, ItemEnhSinTar, PotionEnh)) return TRUE; + // Greater first. This provides +2d4 + 1. + // owls wisdom :-) + if(AI_ActionCastSpell(SPELL_GREATER_OWLS_WISDOM, SpellEnhSinTar)) return TRUE; + // Lesser one - but we have items for it. + if(AI_ActionCastSpell(SPELL_OWLS_WISDOM, SpellEnhSinTar, OBJECT_SELF, i12, ItemEnhSinTar, PotionEnh)) return TRUE; + } + } + else // Monsters probably benifit from this as well. + { + if(!AI_GetAIHaveSpellsEffect(GlobalHasEaglesSpledorSpell) && + !AI_CompareTimeStopStored(SPELL_GREATER_EAGLE_SPLENDOR, SPELL_EAGLE_SPLEDOR)) + { + // Greater first. This provides +2d4 + 1. + // eagles splendor :-) + if(AI_ActionCastSpell(SPELL_GREATER_EAGLE_SPLENDOR, SpellEnhSinTar)) return TRUE; + // Lesser one - but we have items for it. + if(AI_ActionCastSpell(SPELL_EAGLE_SPLEDOR, SpellEnhSinTar, OBJECT_SELF, i12, ItemEnhSinTar, PotionEnh)) return TRUE; + } + } + } + + // Special behaviour: Constructs... + // - These are bastards, and have either total immunity to spells or massive + // SR. + // - GlobalNormalSpellNoEffectLevel will already be set. + if(IsFirstRunThrough && GlobalIntelligence >= i5 && + GlobalSpellTargetRace == CLASS_TYPE_CONSTRUCT) + { + // These spells go through any ristances (I mean, "Spell Immunity: Level 9 or lower") + + // Ruin - (Epic) - 35d6 divine damage, fort save for half. + if(AI_ActionUseFeatOnObject(AI_FEAT_EPIC_SPELL_RUIN, GlobalSpellTarget)) return TRUE; + + // Crumble. Level 6 (Druid) - Up to 15d6 damage to a construct. + if(AI_ActionCastSpell(SPELL_CRUMBLE, SpellHostRanged, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + + // Cast prismatic AC bonus spell - defective force + if(!GetHasSpellEffect(AI_SPELLABILITY_PRISMATIC_DEFLECTING_FORCE)) + { + // Deflecting Force - adds charisma bonus to defeltection AC. + if(AI_ActionCastSpell(AI_SPELLABILITY_PRISMATIC_DEFLECTING_FORCE, SpellProSinTar)) return TRUE; + } + + // Try a finger/destruction spell, if their fortitude save is really, really low. + // Will not use these 2 twice in time stop, as they *should* die instantly + if(GlobalNormalSpellsNoEffectLevel < i7 && IsFirstRunThrough && + GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_INSTANT_DEATH_SPELLS, AI_COMBAT_MASTER) && + GlobalSeenSpell && RangeShortValid && + !AI_CompareTimeStopStored(SPELL_DESTRUCTION, SPELL_FINGER_OF_DEATH)) + { + // Check low saves, IE always fails, no immunities and no mantals. + if((GlobalSpellTargetFort + i20) <= (GlobalSpellBaseSaveDC + i7) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath) && + !AI_GetSpellTargetImmunity(GlobalImmunityMantalProtection)) + { + // Note: No items, because it will be a much lower save. + + // Destruction, level 7 (Cleric). Fort (Death) for Death if fail, or 10d6 damage if pass. + if(AI_ActionCastSpell(SPELL_DESTRUCTION, SpellHostRanged, GlobalSpellTarget, i17)) return TRUE; + // Finger of Death. Leve 7 (Mage). Fort (Death) for death if fail, or d6(3) + nCasterLvl damage if pass. + if(AI_ActionCastSpell(SPELL_FINGER_OF_DEATH, SpellHostRanged, GlobalSpellTarget, i17)) return TRUE; + } + } + + // Now will cast mantal if not got one and nearest enemy is a spellcaster... + if(GlobalMeleeAttackers <= i1 /*We won't cast it with melee attackers - cast phisicals first*/ && + !GlobalInTimeStop && GlobalIntelligence >= i7 && IsFirstRunThrough && + !AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) && + /* Check for mage classes...spell target only */ + (GetLevelByClass(CLASS_TYPE_WIZARD, GlobalSpellTarget) >= GlobalSpellTargetHitDice/i3) && + (GetLevelByClass(CLASS_TYPE_SORCERER, GlobalSpellTarget) >= GlobalSpellTargetHitDice/i3)) + { + // Cast mantals, or spell resistance...or Protection from spells. + if(AI_SpellWrapperMantalProtections()) return TRUE; + + // Protection from spells. Level 7 (Mage), for +8 on all saves (Area effect too!) + if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_SPELLS, SpellProAre, OBJECT_SELF, i17, FALSE, ItemProAre)) return TRUE; + + // Spell resistance. Level 5 (Cleric/Druid) 12 + Caster level (no limit) in spell resistance. + if(AI_ActionCastSpell(SPELL_SPELL_RESISTANCE, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + + if(IsFirstRunThrough) + { + // Haste for allies + // - 60% chance of casting. More below somewhere (near stoneskin + if(AI_ActionCastAllyBuffSpell(f6, i60, SPELL_MASS_HASTE, SPELL_HASTE)) return TRUE; + // Ultravision for allies + // - 90% chance of casting. + // - Only cast if a valid enemy in darkness is near + if(GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_DARKVISION, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD))) + { + // Darkvision (Ultravision) but not trueseeing, is cast. + if(AI_ActionCastAllyBuffSpell(f8, i90, SPELL_DARKVISION, iM1, iM1, iM1, SPELL_TRUE_SEEING)) return TRUE; + } + } + // Epic killing spells. Hellball is very important to fire off! + // - We fire this off at the futhest target in 40M + + // Futhest one away. + if(IsFirstRunThrough && GlobalSeenSpell) + { + // Hellball. (Epic) Long range AOE - 10d6 sonic, acid, fire and lightning damage to all in area. Reflex only halves. + // We just use spell target. Tough luck, we probably will be in the AOE + // whatever target we want! + if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_HELLBALL, SPELL_EPIC_HELLBALL, GlobalSpellTarget)) return TRUE; + + // Ruin - a lot of damage, no SR, and only a half save thing for damage + if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_RUIN, SPELL_EPIC_RUIN, GlobalSpellTarget)) return TRUE; + } + + // These 3 will not be wasted on mantals. + if(IsFirstRunThrough && GlobalNormalSpellsNoEffectLevel < i9 &&// PW kill is level 9 + !AI_GetSpellTargetImmunity(GlobalImmunityMantalProtection) && + !AI_CompareTimeStopStored(SPELL_CLOUDKILL, SPELL_POWER_WORD_KILL, SPELL_CIRCLE_OF_DEATH)) + { + // INSTANT DEATH SPELLS...if conditions are met. + // - These will instantly kill a target. It does cheat to do it. Only + // does this if set (Not AI intelligence related) + // Most useful for high-levels not getting killed by much lower levels + // (EG: Lich getting defeated by a enemy with only knockdown or something) + if(GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_INSTANT_DEATH_SPELLS, AI_COMBAT_MASTER)) + { + // Cloudkill here - if average HD is < 7 + // Random is < 7, always if under 4 + // Damage, slow, long range. Also, AOE :-) + if(RangeLongValid && (GlobalAverageEnemyHD < i4) || + ((GlobalAverageEnemyHD < i7) && (d10() < i4)) && + GlobalNormalSpellsNoEffectLevel < i5) + { + // Cloud Kill. Level 5 (Mage). Kills if under level 7, else acid damage. + if(AI_ActionCastSpell(SPELL_CLOUDKILL, SpellHostAreaInd, GlobalSpellTarget, i15, TRUE, ItemHostAreaInd)) return TRUE; + } + // Power Word: Kill + // If improved, will check HP, else just casts it. + if(RangeShortValid && GlobalSpellTargetCurrentHitPoints < i100) + { + // Power Word Kill. Level 9 (Mage). If target has less then 100HP, dies. + if(AI_ActionCastSpell(SPELL_POWER_WORD_KILL, SpellHostAreaInd, GlobalSpellTarget, i19, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Circle of Death + // CONDITION: Average enemy party hd less than 10 + // This spell only effects level 1-9 people. Good cleaner for lower monsters! + // Meduim ranged spell. + if(RangeMediumValid && GlobalAverageEnemyHD <= i9 && + GlobalNormalSpellsNoEffectLevel < i6) + { + // Circle of death. Level 6 (Mage). Medium ranged, Large AOE, kills 10HD or less people (to d4(casterlevel)). + if(AI_ActionCastSpell(SPELL_CIRCLE_OF_DEATH, SpellHostAreaDis, GlobalSpellTarget, i16, TRUE, ItemHostAreaDis)) return TRUE; + } + } + + if(IsFirstRunThrough) + { + // Physical Damage Protections (Self) + // Stoneskins, (Greater, normal) + Premonition provide instant damage reduction. + if(AI_SpellWrapperPhisicalProtections(iLowestSpellLevel)) return TRUE; + } + + + // Do not check spell resistance (GlobalNormalSpellsNoEffectLevel) here. + // We only use pulses 80% of the time. + if(RangeTouchValid && !GlobalInTimeStop && + GlobalSpellTargetRange < fTouchRange && d10() <= i8) + { +/* These are the pulses. Not much I can be bothered to check for. All good stuff! + 281 | Pulse_Drown - None Constructs, Undeads, Elementals DIE if fail a fort check, DC: 20. + 282 | Pulse_Spores - Soldier Shakes (Disease) is applied to those in the area. + 283 | Pulse_Whirlwind - Large. Reflex DC 14 check, or Knockdown (2 rounds) and d3(HD) Damage. + 284 | Pulse_Fire - Huge. d6(HD) in fire damage. Reflex Save, DC: 10 + HD + 285 | Pulse_Lightning - Huge. d6(HD) in electrical damage. Reflex Save, DC: 10 + HD + 286 | Pulse_Cold - Huge. d6(HD) in cold damage. Reflex Save, DC: 10 + HD + 287 | Pulse_Negative - Large. Heals (Undead) allies. Harms all non-undead. Damage/Heal: d4(HD). Reflex Save, DC: 10 + HD. + 288 | Pulse_Holy - Large. Heals (Non-undead) allies. Harms all undead. Damage/Heal: d4(HD). Reflex Save, DC: 10 + HD. + 289 | Pulse_Death - Large. Death if fail Fort Save, DC: 10 + HD + 290 | Pulse_Level_Drain - 1 Negative Level, if fail Fort Save, DC: 10 + HD. + 291 | Pulse_Ability_Drain_Intelligence - Large. -(HD/5) INT, if fail fort save VS: 10 + HD. + 292 | Pulse_Ability_Drain_Charisma - Large. -(HD/5) CHA, if fail fort save VS: 10 + HD. + 293 | Pulse_Ability_Drain_Constitution - Large. -(HD/5) CON, if fail fort save VS: 10 + HD. + 294 | Pulse_Ability_Drain_Dexterity - Large. -(HD/5) DEX, if fail fort save VS: 10 + HD. + 295 | Pulse_Ability_Drain_Strength - Large. -(HD/5) STR, if fail fort save VS: 10 + HD. + 296 | Pulse_Ability_Drain_Wisdom - Large. -(HD/5) WIS, if fail fort save VS: 10 + HD. + 297 | Pulse_Poison - Varying Poison instantly applied. Check nw_s1_pulspois.nss, and poison.2da files. + 298 | Pulse_Disease - Varying Save. See diseases.2da, and it is based on the + race of the caster, below are the diseases applied: + Vermin = Vermin Madness. Undead = Filth Fever. Outsider = Demon Fever. + Magical Beast = Soldier Shakes. Aberration = Blinding Sickness. + ANYTHING ELSE = Mindfire. */ + for(iCnt = SPELLABILITY_PULSE_DROWN/*281*/; iCnt <= SPELLABILITY_PULSE_DISEASE/*289*/; iCnt++) + { + // All innate, so no matter about talents really. + if(AI_ActionCastSpell(iCnt)) return TRUE; + } + } + + // Then harm/heal. (Needs 20 HP, and be challenging to us). + if((GlobalAverageEnemyHD >= (GlobalOurHitDice - i5)) && + GlobalSpellTargetCurrentHitPoints > i20 && RangeTouchValid && + !AI_CompareTimeStopStored(SPELL_HARM, SPELL_MASS_HEAL, SPELL_HEAL) && + GlobalNormalSpellsNoEffectLevel < i8)// Mass heal = 8. + { + if(GlobalSeenSpell && GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD && + GlobalSpellTargetRace != RACIAL_TYPE_CONSTRUCT && + GlobalSpellTargetRace != RACIAL_TYPE_INVALID) + { + // If we are undead, we make sure we leave at least 1 for our own healing. + if(GlobalNormalSpellsNoEffectLevel < i6 && + (GlobalOurRace != RACIAL_TYPE_UNDEAD || GetHasSpell(SPELL_HARM) >= i2)) + { + // Harm + // CONDITION: 6+ hit dice and NOT undead! :) Also checks HP + // Harm Level 6 (Cleric) 7 (Druid) Makes the target go down to 1d4HP (or heals undead as heal) + if(AI_ActionCastSpell(SPELL_HARM, SpellHostTouch, GlobalSpellTarget, i16, FALSE, ItemHostTouch)) return TRUE; + } + } + // (Mass) Heal (used as Harm for undead) + // CONDITION: Undead at 4+ hd. Never casts twice in time stop, and not over 20 HP. + else if(GlobalSpellTargetRace == RACIAL_TYPE_UNDEAD && + GlobalIntelligence >= i7) + { + // Really, talent 4, heal area effect, no items are set in this though. + // Mass Heal. Level 8 (Cleric) 9 (Druid) mass "heal" damage/healing. + if(AI_ActionCastSpell(SPELL_MASS_HEAL, FALSE, GlobalSpellTarget, i18, TRUE)) return TRUE; + + // Never use last 2 heals for harming. Level 6 (Cleric) 7 (Druid) + // - 1.3, changed to 3+ only, because, basically, healing self will + // probably be better. Undead + Constructs ignore this and use all of them. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i6 && + (GetHasSpell(SPELL_HEAL) >= i3 || GlobalOurRace == RACIAL_TYPE_UNDEAD || + GlobalOurRace != RACIAL_TYPE_CONSTRUCT)) + { + // Heal. Level 6 (Cleric) 7 (Druid). For undead: As harm, else full healing. + if(AI_ActionCastSpell(SPELL_HEAL, FALSE, GlobalSpellTarget, i16)) return TRUE; + } + } + } + + // Power Word: Stun + // Is not immune to mind spell (I think this is a valid check) and not already stunned. + // Really, if under < 151 HP to be affected + // Wierdly, this is considered a "Area effect" spell. Nope - jsut a VERY nice normal one. (I like it!) + // - We can cast this later. Here, we only cast if low amount of enemies :-D + if(GlobalSpellTargetCurrentHitPoints <= i150 && + GlobalNormalSpellsNoEffectLevel < i7 && + GlobalTotalSeenHeardEnemies < i3 && GlobalAverageEnemyHD > i10 && + GlobalSeenSpell && RangeTouchValid && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_CompareTimeStopStored(SPELL_POWER_WORD_STUN)) + { + // Power Word Stun. Level 7 (Wizard). Stun duration based on HP. + if(AI_ActionCastSpell(SPELL_POWER_WORD_STUN, SpellHostAreaInd, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE; + } + + // Elemental shield here, if over 0 melee attackers (and 30% chace) or + // over 1-4 attackers (level based). Doesn't double cast, and not more then 1 at once. + if(IsFirstRunThrough && + // - Checks if we have the effects or not in a second + ((GlobalMeleeAttackers > (GlobalOurHitDice / i4) || + (GlobalMeleeAttackers > i0 && d10() > i6))) && + !AI_CompareTimeStopStored(SPELL_ELEMENTAL_SHIELD, SPELL_WOUNDING_WHISPERS, + SPELL_DEATH_ARMOR, SPELL_MESTILS_ACID_SHEATH)) + { + if(AI_SpellWrapperShieldProtections(iLowestSpellLevel)) return TRUE; + } + + // Dispel Good spells on the enemy. + // - Dispel Level 5 here. + // Basically, level 5 spells are mantals and spell-stoppers, or an alful + // lot of lower level spells. + if(RangeMediumValid && !GlobalInTimeStop)// All medium spells. + { + // Dispel number need to be 5 for breach + if(GlobalDispelTargetHighestBreach >= i5) + { + // Wrapers Greater and Lesser Breach. + if(AI_ActionCastBreach()) return TRUE; + } + // Dispel >= 5 + if(GlobalDispelTargetHighestDispel >= i5) + { + // Wrappers the dispel spells + if(AI_ActionCastDispel()) return TRUE; + } + } + + // Consealment Protections + // We do displacement then blindness/deafness. We only attempt blindness/deafness + // if we are not a sorceror/bard mind you. + if(IsFirstRunThrough && !GlobalInTimeStop && // No Consealment in time stop. + GetObjectSeen(OBJECT_SELF, GlobalSpellTarget) && + !AI_GetAIHaveEffect(GlobalEffectInvisible) && + !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells)) + { + // Imp. Invis and displacement... + if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF, iLowestSpellLevel)) return TRUE; + + // Cast darkness on any enemy in range, if we have ultravision (or its + // effects) + // Need range check, with SRA, of course + if(RangeLongValid && (AI_GetAIHaveEffect(GlobalEffectUltravision) || + AI_GetAIHaveEffect(GlobalEffectTrueSeeing) || + GetHasSpell(SPELL_DARKVISION) || GetHasSpell(SPELL_TRUE_SEEING))) + { + // Cast at nearest without darkness! + oAOE = GetNearestCreature(CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, SPELL_DARKVISION, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + if(GetIsObjectValid(oAOE) && !GetHasSpellEffect(SPELL_TRUE_SEEING, oAOE) + && !GetHasSpellEffect(SPELL_DARKVISION, oAOE)) + { + // Darkness + if(AI_SpellWrapperDarknessSpells(oAOE)) return TRUE; + } + } + + // Blindness/deafness. No casting in time stop is simpler. + if(RangeMediumValid && GlobalNormalSpellsNoEffectLevel < i8 && + !AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) && + !GlobalWeAreSorcerorBard && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i8)) + { + // Mass Blindness and Deafness. Level 8 (Mage) AOE fort save, enemies only, who save or are blinded and deafened. + if(AI_ActionCastSpell(SPELL_MASS_BLINDNESS_AND_DEAFNESS, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE; + } + } + + // Ally spells. A great variety that we cast above. + if(IsFirstRunThrough) + { + // Haste again - 90% chance + if(AI_ActionCastAllyBuffSpell(f6, i90, SPELL_MASS_HASTE, SPELL_HASTE)) return TRUE; + // Cast phisical stoneskins on allies - 80% chance, as it is quite good. + if(AI_ActionCastAllyBuffSpell(f6, i80, SPELL_GREATER_STONESKIN, SPELL_STONESKIN)) return TRUE; + // Consealment spells + if(AI_ActionCastAllyBuffSpell(f6, i60, SPELL_IMPROVED_INVISIBILITY, SPELL_DISPLACEMENT)) return TRUE; + } + + //Gate + //CONDITION: Protection from Evil active on self + // Balors rock, literally! If not, I kill dem all!! >:-D + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i10 && + SpellAllies && AI_GetAIHaveSpellsEffect(GlobalHasProtectionEvilSpell)) + { + // Gate. Level 9 (Mage/Innate). Summons a balor, who would normally attack caster if not protected from evil, else powerful summon + if(AI_ActionCastSummonSpell(SPELL_GATE, i19, i10)) return TRUE; + } + + //Protection from Evil / Magic Circle Against Evil + // ... in preparation for Gate! + if(IsFirstRunThrough &&GetHasSpell(SPELL_GATE) && + !AI_GetAIHaveSpellsEffect(GlobalHasProtectionEvilSpell)) + { + // Magic Circle against Alignment. Level 3 (Mage/Cleric/Bard/Paladin/Innate). +2 AC, mind spell immunity VS them. + if(AI_ActionCastSubSpell(SPELL_MAGIC_CIRCLE_AGAINST_EVIL, SpellEnhAre, OBJECT_SELF, i13, FALSE, ItemEnhAre)) return TRUE; + // Protection From Evil. Level 1(Mage/Cleric/Bard/Paladin/Innate). +4 AC, mind spell immunity VS them. + if(AI_ActionCastSubSpell(SPELL_PROTECTION_FROM_EVIL, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSelf, PotionPro)) return TRUE; + } + // None for allies. Won't bother (unless it is a problem!). + // - If adding, we will get the nearest without the spells effects. + + // GlobalCanSummonSimilarLevel can be 1-12. (10 is elemental swarm, balor) (11, 12 epic) + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i9) + { + // Elemental swarm. Level 9 (Druid) Never replaced until no summon left - summons consecutive 4 huge elementals + if(AI_ActionCastSummonSpell(SPELL_ELEMENTAL_SWARM, i19, i10)) return TRUE; + // Summon an eldar elemental. + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_IX, i19, i9)) return TRUE; + // No-concentration summoned creatures. + if(AI_ActionCastSummonSpell(AI_SPELLABILITY_SUMMON_BAATEZU, FALSE, i9)) return TRUE; + if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_TANARRI, FALSE, i9)) return TRUE; + if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_SLAAD, FALSE, i9)) return TRUE; + if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_CELESTIAL, FALSE, i9)) return TRUE; + if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_MEPHIT, FALSE, i9)) return TRUE; + if(AI_ActionCastSummonSpell(SPELLABILITY_NEGATIVE_PLANE_AVATAR, FALSE, i9)) return TRUE; + } + + // Dominate spells + // - Dominate monster is level 9. Others are worth casting if valid. + // Needs to not be immune, and be of right race for DOM PERSON + // No time stop, and a valid will save to even attempt. + if(!GlobalInTimeStop && GlobalSeenSpell && RangeMediumValid && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + (GlobalOurHitDice - GlobalAverageEnemyHD) <= i8) + { + // Will save VS mind spells. + if(GlobalNormalSpellsNoEffectLevel < i9 && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i9)) + { + // Dominate Monster. Level 9 (Mage) Dominates (VS. Mind will save) ANYTHING! + if(AI_ActionCastSpell(SPELL_DOMINATE_MONSTER, SpellHostRanged, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; + + if(GlobalNormalSpellsNoEffectLevel < i5 && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i5)) + { + // Dominate person + if(!GetIsPlayableRacialType(GlobalSpellTarget)) + { + // Dominate Person. Level 5 (Mage). Only affects PC races. Dominate on failed will. + if(AI_ActionCastSpell(SPELL_DOMINATE_PERSON, SpellHostRanged, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Mass charm. sorcerors/bards have better things (20% casting chance) + if(!GlobalWeAreSorcerorBard || d10() <= i2) + { + // For mass charm, we don't bother if they don't have a decent chance to fail (ues as level 5 spell for save DC) + // Mass Charm. Level 8 (Mage) 1 Round/2caster levels of charm. Affects PC races only + humanoids. + if(AI_ActionCastSpell(SPELL_MASS_CHARM, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE; + } + } + } + } + + // Protection from Spells + // - CONDITION: Enemies less than 5 levels below me (powerful enemies) + // - CONDITION 2: At least 1 ally + // - We cast this lower down if we don't cast it here. + if(IsFirstRunThrough && + (((GlobalOurHitDice - GlobalAverageEnemyHD) <= i5 && + GlobalValidSeenAlly && GlobalRangeToAlly < f5) || + ((GlobalOurHitDice - GlobalAverageEnemyHD) <= i10))) + { + if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_SPELLS, SpellProAre, OBJECT_SELF, i17, FALSE, ItemProAre)) return TRUE; + } + + //Mantle Protections + //CONDITION: Enemies less than 5 levels below me (powerful enemies) + // Will chance casting these anyway, if no melee attackers, and the enemy has a valid talent. + // Yes, talents include items...ahh well. + if(IsFirstRunThrough && ((GlobalOurHitDice - GlobalAverageEnemyHD) <= i5)) + { + if(AI_SpellWrapperMantalProtections(iLowestSpellLevel)) return TRUE; + } + + // All these are short-ranged death! + // - What do we want to cast? Eh? Well, we might as well randomly choose + // one of the level 9 main hostile spells. This includes single target spells, + // BUT we may use sigle target spells first if not many enemies. + + // - Energy Drain (powerful, lowers statistics) + // - Bigby's Crushing Hand (2d6 + 12 damage/round) + + // - Storm of vengance is a massive electrical/acid storm. Cast, we may move out later. + // - Implosion has a +3 fort Save DC (great!) (only medium radius) + // - Wail of the banshee has a collosal area and 1 save. + // - Wierd doesn't affect allies, 2 saves, collosal area. + // - Meteor Storm is like a huge fireball based on the caster, 20d6 damage. + + // Single target spells first check + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i9) + { + // Crushing hand is indefinatly better, but depends...I think energy drain + // has its merits of being permament! :-D + +// Explanation on bigby's spells - hitting, grappling. +// HIT: Beat GetAC(oTarget) with Primary Stat + Caster Level + d20 + 9-11 (7, 8, 9 level spells) +// GRAPPLE: Beat BAB + Size Mod of opposing. d20 + nCasterModifier + Caster Level + 14-16. +// Basically, we won't check this, as the caster level + the 9-11 + primary stat can beat AC. +// Ok, I can't be bothered to check it :-P + + // 70% chance of Crushing Hand. + // We try and not cast it twice on the same target (for a starter, wasting spells) + if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_CRUSHING_HAND, GlobalSpellTarget)) + { + // Bigby's Crushing Hand. Level 9 (Mage). 2d6 + 12 damage/round. + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CRUSHING_HAND, SpellHostRanged, i60, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; + } + + if(RangeShortValid && GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i9)) + { + // 40% chance of energy drain, as we think single target spells are better. + // Energy Drain. Level 9 (Mage). 2d4 negative levels! -BAB, Saves, Stats! :-) + if(AI_ActionCastSpellRandom(SPELL_ENERGY_DRAIN, SpellHostRanged, i40, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; + } + + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + + // AOE targets. Most...but not all...are short-range spells. + if(RangeShortValid) // No GlobalNormalSpellsNoEffectLevel for this. AOE spells may affect someone who is not immune! + { + // Storm - a very good AOE spell. May as well use here! + // AOE is 10M across. + // - May add in elemental protections check. + // 40% chance (especially as it is only a clerical spell) + if(!GlobalInTimeStop && GlobalSpellTargetRange < f6) + { + if(AI_ActionCastSpellRandom(SPELL_STORM_OF_VENGEANCE, SpellHostAreaDis, i40, OBJECT_SELF, i19, TRUE, ItemHostAreaDis)) return TRUE; + } + // Implosion - great spell! Instant death on a +3 save (!). Short range. + if(SpellHostAreaInd && (GetHasSpell(SPELL_IMPLOSION) || + ItemHostAreaInd == SPELL_IMPLOSION)) + { + // Its save is at 9 + 3 = 12 DC. Death save, and can kill allies not in PvP + // - Note that because we check natural "globes" in this, the level is set to 9. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_MEDIUM, i9, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly, TRUE); + // If valid, 40% chance of casting this one before others. + if(GetIsObjectValid(oAOE)) + { + // Implosion. Level 9 (Cleric). Instant death at +3 save. Note: Medium radius (others are collosal) + if(AI_ActionCastSpellRandom(SPELL_IMPLOSION, SpellHostAreaInd, i30, oAOE, i19, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Wail of the Banshee + // Fort save, else death, and it never affects us, but can kill allies. + if(SpellHostAreaDis && (GetHasSpell(SPELL_WAIL_OF_THE_BANSHEE) || + ItemHostAreaDis == SPELL_WAIL_OF_THE_BANSHEE)) + { + // Collosal range, fortitude, necromancy and death saves. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_COLOSSAL, i9, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly, TRUE, TRUE); + // If valid, 40% of firing. + if(GetIsObjectValid(oAOE)) + { + // Wail of the Banshee. Level 9 (Mage/Innate). Caster cries out, kills everything that cannot save in area affected. + if(AI_ActionCastSpellRandom(SPELL_WAIL_OF_THE_BANSHEE, SpellHostAreaDis, i30, oAOE, i19, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Weird - item immunity fear? Need to test + // Never affects allies. Will save type - if the will is always + // saved, it does nothing at all. + if(SpellHostAreaDis && (GetHasSpell(SPELL_WEIRD) || + ItemHostAreaDis == SPELL_WEIRD)) + { + // Get AOE object - this is a small (8M) range, collosal size, doesn't affect allies. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_COLOSSAL, i9, SAVING_THROW_WILL); + // Is it valid? 40% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Wierd. Level 9 (Wizard/Innate). 2 saves (will/fort) against death. Doesn't kill allies! (Illusion) + if(AI_ActionCastSpellRandom(SPELL_WEIRD, SpellHostAreaDis, i30, oAOE, i19, TRUE, ItemHostAreaDis)) return TRUE; + } + } + //Meteor Swarm + // CONDITION: Closest enemy 5 ft. (1.5 meters) from me + // Changed to 10M ... the collosal sized actially used (on self) + // But only if the enemy is. Removed enemy fire, for now. + // 40% chance. + if(GlobalSpellTargetRange < f5) + { + if(AI_ActionCastSpellRandom(SPELL_METEOR_SWARM, SpellHostAreaDis, i30, OBJECT_SELF, i19, TRUE, ItemHostAreaDis)) return TRUE; + } + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Finally, the single target spells again. IE cast them after the AOE ones + // if we don't choose an AOE one. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i9) + { + // 50% chance of Crushing Hand. + // We try and not cast it twice on the same target (for a starter, wasting spells) + if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_CRUSHING_HAND, GlobalSpellTarget)) + { + // Bigby's Crushing Hand. Level 9 (Mage). 2d6 + 12 damage/round. + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CRUSHING_HAND, SpellHostRanged, i40, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; + } + + // Short range, not undead ETC. + if(RangeShortValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i9)) + { + // 30% chance of energy drain + // Energy Drain. Level 9 (Mage). 2d4 negative levels! -BAB, Saves, Stats! :-) + if(AI_ActionCastSpellRandom(SPELL_ENERGY_DRAIN, SpellHostRanged, i30, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; + } + } + + // Backup, obviously, that we might not randomly choose Implision, if it + // fails the 40% check. Might sitll have the item/spell though! + if(AI_ActionCastBackupRandomSpell()) return TRUE; + + if(IsFirstRunThrough) + { + // Finally, we might as well cast that new (SoU) cleric spell which stops + // many negative effects :-) + // Undeath's Eternal Foe. Stops negative damage, ability score draining, + // negative levels, immunity poisons, immunity diseases. + // Level 9 (Cleric) + if(AI_ActionCastSpell(SPELL_UNDEATHS_ETERNAL_FOE, SpellEnhSinTar, OBJECT_SELF, i19, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + } +/*::8888888888888888888888888888888888888888888888888888888888888888888888888888 + Level 8 Spells. +//::8888888888888888888888888888888888888888888888888888888888888888888888888888 + Thoughts - LOTS of AOE spells here for all classes. *shrugs* all good too! + massive damage and decent saves. Will random cast most of them. Ones for + Sorcerors that they'll want to random cast are [Sunburst] (100% chance VS undead!) + [Incendiary Cloud], [Horrid Wilting] (both are decent damage) [Clenched fist]. + + AOE: + [Natures Balance] - -1d4(CasterLevel/5) SR for enemies. 3d8 + Caster Level in healing for allies. Large area. Short range. Druid only. + [Sunbeam] - Blindness VS Reflex. d6(CasterLevel) divine VS undead, else 3d6 others. ReactionType. +S [Sun Burst] - Similar to sunbeam. 6d6 to others. Vampires die + all undead d6(Caster level) damage. +S [Earthquake] - 1d6(caster level) in damage to AOE, except caster. + [Fire Storm] - 1d6(Caster level) in fire/divine damage. No allies. collosal over caster. +S [Bombardment] - 1d8(caster level) in fire damage. Like Horrid Wilting. Reflex save/long range/collosal + X [Mass Charm] - Charm a lot of enemies in an area. Cast above + X [Mass Blindness/Deafness] - Blind and death on fortitude save. Cast above + [Incendiary Cloud] - 4d6 Fire damage/round in the large AOE. + [Horrid Wilting] - d8(CasterLevel) in negative energy. Fort for half. Necromancy. + + Single: +S [Bigby's Clenched Fist] - Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well. + + Defender: + [Aura Versus Alignment] - d6 + d8 damage shield, 25SR, 4Saves, 4AC, mind immune VS alignemnt + X [Premonition] - 30/+5 DR. Cast up above this, though. + X [Mind blank] - Mind immunity/cleansing. Not cast normally, except in invisbility. +S X [Etherealness] - Total invisiblity (Bugged I say). Cast above. + + Summon: + [Create Greater Undead] - Create Vampire, Doom knight, Lich, Mummy Cleric. + [Summon Creature VIII (8)] - Greater Elemental. + [Greater Planar Binding] - Death Slaad (Evil) Vrock (Neutral) Trumpet Archon (Good) + + Other: + + Undead are targeted here by undead-specific spells more often the PC's. + + Regenerate is cast if we have damage. + + Gazes (monster gazes) are powerful AOE cone spells. Cast at the taget. +//::88888888888888888888888888888888888888888888888888888888888888888888888888*/ + + // Jump out if we don't want to cast level 8 spells/abilities. + if(iLowestSpellLevel > i8) return FALSE; + + int iEnemyAlignment = GetAlignmentGoodEvil(GlobalSpellTarget); + if(IsFirstRunThrough) + { + // Aura Vs. Alignment. d6 + d8 damage shield, 25SR, 4Saves, 4AC, mind immune VS alignemnt + if(GetAlignmentGoodEvil(GlobalSpellTarget) == ALIGNMENT_EVIL) + { + // Holy: Versus Evil. Level 8 (Cleric) + if(AI_ActionCastSubSpell(SPELL_HOLY_AURA, SpellEnhSelf, OBJECT_SELF, i18, FALSE, ItemEnhSelf)) return TRUE; + } + else if(iEnemyAlignment == ALIGNMENT_GOOD) + { + // Unholy Holy: Versus Good. Level 8 (Cleric) + if(AI_ActionCastSubSpell(SPELL_UNHOLY_AURA, SpellEnhSelf, OBJECT_SELF, i18, FALSE, ItemEnhSelf)) return TRUE; + } + } + + // Cast regeneration + Natures Balance here if we are lacking HP. + if(IsFirstRunThrough && GlobalOurPercentHP <= i70) + { + if(!GetHasSpellEffect(SPELL_REGENERATE)) + { + // Regeneration. Level 6 (Druid) 7 (Cleric). 6HP/Round. Good for persistant healing. + if(AI_ActionCastSpell(SPELL_REGENERATE, SpellEnhSelf, OBJECT_SELF, i17, FALSE, ItemEnhSelf, PotionEnh)) return TRUE; + } + // Cast at us, might as well. + // Natures Balance. Level 8 (Druid). Lowers SR of enemies by 1d4(CasterLevel/5) + Healing (3d6 + CasterLevel) for allies. + if(AI_ActionCastSpell(SPELL_NATURES_BALANCE, SpellHostAreaDis, OBJECT_SELF, i18, TRUE, ItemHostAreaDis)) return TRUE; + } + + // Undead spells. We can cast sunbeam/sunburst (VERY similar!) and Searing Light. + // We do cast this against non-undead, but later on. + // We only do this if at 5+ intelligence. Those below are stupid :-) + if(GlobalSpellTargetRace == RACIAL_TYPE_UNDEAD && + GlobalIntelligence >= i5 && GlobalNormalSpellsNoEffectLevel < i8) + { + // First, Undead to Death - Slays 1d4HD worth of undead/level. + // (Max 20d4). Lowest first. + // - Will save! + // - 20M radius (but this we can ignore) + // - Takes into account all SR and so forth. + if(GlobalNormalSpellsNoEffectLevel < i6 && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i6) && + RangeMediumValid) + { + // Undead to Death. Level 6 (Cleric) Slays 1d4HD worth of undead/level. (Max 20d4). Lowest first. + if(AI_ActionCastSpell(SPELL_UNDEATH_TO_DEATH, SpellHostAreaDis, GlobalSpellTarget, i16, TRUE, ItemHostAreaDis)) return TRUE; + } + // SUNBURST: 6d6 to non-undead. Kills vampires. Blindness. Limit of 25 dice for undead damage. Medium range/colosal size + // SUNBEAM: 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size + // Really silly. Only druids/mages get SunBurst...and druids get Sunbeam anyway! + // *sigh* Only difference is sunburst has a higher limit for damage, kills + // vampires, and does 6d6 damage to non-undead, so marginally better. + + // Won't even randomly choose between them. Not worth it. + if(RangeShortValid) + { + // Sunburst. Level 8 (Druid/Mage) 6d6 to non-undead. Kills vampires. Blindness. Limit of 25 dice for undead damage. Medium range/colosal size + if(AI_ActionCastSpell(SPELL_SUNBURST, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE; + // Sunbeam. Level 8 (Cleric/Druid) 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size + if(AI_ActionCastSpell(SPELL_SUNBEAM, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE; + + // If we can't destroy them, dominate them! + if(GlobalSpellTargetHitDice < GlobalOurChosenClassLevel * i3 && + GlobalNormalSpellsNoEffectLevel < i8 && + !GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_DOMINATED))) + { + // Control undead. Level 6 (Cleric) 7 (Mage). Dominates 1 undead up to 3x Caster level. + if(AI_ActionCastSpell(SPELL_CONTROL_UNDEAD, SpellHostRanged, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE; + } + } + // Medium spell + if(GlobalNormalSpellsNoEffectLevel < i3) + { + // Searing light. Level 3 (Cleric). Full level: 1-10d6 VS undead. Half Level: 1-5d6 VS constructs. 1-5d8 VS Others. + if(AI_ActionCastSpell(SPELL_SEARING_LIGHT, SpellHostRanged, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + } + + // Gazes have short ranges - 80% chance. + // Why this high? (compared to say, fire storm?) because they are innate + // and so no concentration checks, and pretty good DC's. + if(RangeShortValid && d10() <= i8) // No GlobalNormalSpellsNoEffectLevel check + { + // We cast all of these, but randomly. It works through with most powerful + // getting the highest %'s of course :-) + if(!AI_GetSpellTargetImmunity(GlobalImmunityDeath)) + { + // Death gazes first - Golem one is the most deadly! + // 50% chance of either. + if(AI_ActionCastSpellRandom(SPELLABILITY_GOLEM_BREATH_GAS, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DEATH, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + // Petrify - SoU. 50% chance (almost like death!) + if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify)) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_PETRIFY, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + // Destroy X are powerful. 50% chance each. They are basically as DEATH but for alignments. + if(iEnemyAlignment == ALIGNMENT_GOOD) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_GOOD, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + else if(iEnemyAlignment == ALIGNMENT_EVIL) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_EVIL, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + if(GetAlignmentLawChaos(GlobalSpellTarget) == ALIGNMENT_CHAOTIC) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_CHAOS, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + else if(GetAlignmentLawChaos(GlobalSpellTarget) == ALIGNMENT_LAWFUL) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_LAW, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + // Can't be immune to mind + if(!AI_GetSpellTargetImmunity(GlobalImmunityMind)) + { + // Fear (and fixed: Added Krenshar Scare) 30% + if(!AI_GetSpellTargetImmunity(GlobalImmunityFear)) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_KRENSHAR_SCARE, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_FEAR, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + // Domination/Charm 30% + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination)) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DOMINATE, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_CHARM, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + // Other random mind things + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_PARALYSIS, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_STUNNED, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_CONFUSION, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DOOM, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + } + + // Level 8 summons. 20HD or under, or 2 melee enemy and under. + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i8 && + (GlobalOurHitDice <= i20 || GlobalMeleeAttackers <= i2)) + { + // Create Greater undead - Pale Master + if(AI_ActionCastSummonSpell(AI_FEAT_PM_CREATE_GREATER_UNDEAD, iM1, i8)) return TRUE; + + // Create Greater Undead. Level 8 (Cleric) Create Vampire, Doom knight, Lich, Mummy Cleric. + if(AI_ActionCastSummonSpell(SPELL_CREATE_GREATER_UNDEAD, i18, i8)) return TRUE; + // Summon an Greater elemental. Summon 8 - Druid/Cleric/Bard/Mage. + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VIII, i18, i8)) return TRUE; + // Greater Planar Binding. Level 8 (Mage) Death Slaad (Evil) Vrock (Neutral) Trumpet Archon (Good) + if(AI_ActionCastSummonSpell(SPELL_GREATER_PLANAR_BINDING, i18, i8)) return TRUE; + } + + // Level 8 general attack spells. + // Randomly pick one: +// [Sunbeam] - Blindness VS Reflex. d6(CasterLevel) divine VS undead, else 3d6 others. ReactionType. +// [Sun Burst] - Similar to sunbeam. 6d6 to others. Vampires die + all undead d6(Caster level) damage. +// [Earthquake] - 1d6(caster level) in damage to AOE, except caster. +// [Fire Storm] - 1d6(Caster level) in fire/divine damage. No allies. collosal over caster. +// [Bombardment] - 1d8(caster level) in fire damage. Like Horrid Wilting. Reflex save/long range/collosal +// [Incendiary Cloud] - 4d6 Fire damage/round in the large AOE. +// [Horrid Wilting] - d8(CasterLevel) in negative energy. Fort for half. Necromancy. + +// Single: +// [Bigby's Clenched Fist] - Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well. + + // Is it best to cast Single Spells First? + // 70% if favourable. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i8) + { + if(!GetHasSpellEffect(SPELL_BIGBYS_CLENCHED_FIST, GlobalSpellTarget) && + !AI_CompareTimeStopStored(SPELL_BIGBYS_CLENCHED_FIST) && + RangeLongValid) + { + // Bigby's Clenched Fist. Level 8 (Mage) Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well. + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CLENCHED_FIST, SpellHostRanged, i60, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE; + } + // No other single spells for level 8. + + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + + + // Area of effect spells. Go through - some do have higher %'s then othres. + + // Fire storm - cast on self with a 10M range around caster (circle). 60% chance. + if(RangeShortValid && GlobalSpellTargetRange <= f8 && + GlobalNormalSpellsNoEffectLevel < i8 && + !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i8)) + { + // Fire storm. Level 8 (Cleric). 1d6(Caster level) in fire/divine damage. No allies. collosal over caster. + if(AI_ActionCastSpellRandom(SPELL_FIRE_STORM, SpellHostAreaDis, i50, OBJECT_SELF, i18, TRUE, ItemHostAreaDis)) return TRUE; + } + // Horrid Wilting + // Never affects allies. Fortitude - necromancy spell too. Lots of damage. + if(SpellHostAreaDis && RangeMediumValid && + (GetHasSpell(SPELL_HORRID_WILTING) || ItemHostAreaDis == SPELL_HORRID_WILTING)) + { + // Won't cast if got lots of undead. 20M range, huge radius. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i8, SAVING_THROW_FORT, SHAPE_SPHERE, FALSE, FALSE, TRUE); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Horrid Wilting. Level 8 (Mage). d8(CasterLevel) in negative energy. Fort for half. Necromancy. + if(AI_ActionCastSpellRandom(SPELL_HORRID_WILTING, SpellHostAreaDis, i50, oAOE, i18, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Bombardment. Similar to the above. Long range, relfex save, not affect allies. + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_BOMBARDMENT) || ItemHostAreaInd == SPELL_BOMBARDMENT)) + { + // 40M range, collosal area. Relfex save. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Bombardment. Level 8 (Druid) 1d8(caster level) in fire damage. Like Horrid Wilting. Reflex save/long range/collosal + if(AI_ActionCastSpellRandom(SPELL_BOMBARDMENT, SpellHostAreaInd, i50, oAOE, i18, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Earthquake. No SR, long range, affects anyone except caster. + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_EARTHQUAKE) || ItemHostAreaInd == SPELL_EARTHQUAKE)) + { + // 40M range, collosal area. Relfex save. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Earthquake] - 1d6(caster level) (to 10d6) in damage to AOE, except caster. + if(AI_ActionCastSpellRandom(SPELL_EARTHQUAKE, SpellHostAreaInd, i40, oAOE, i18, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Incendiary Cloud. Long range, the AOE is 5.0M across. 4d6Damage/round is quite good. + if(SpellHostAreaInd && RangeLongValid && + !AI_CompareTimeStopStored(SPELL_INCENDIARY_CLOUD) && + (GetHasSpell(SPELL_INCENDIARY_CLOUD) || ItemHostAreaInd == SPELL_INCENDIARY_CLOUD)) + { + // 40M range, lagre (5.0 across) area. Relfex saves. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i8, SAVING_THROW_REFLEX); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Earthquake] - 1d6(caster level) (to 10d6) in damage to AOE, except caster. + if(AI_ActionCastSpellRandom(SPELL_INCENDIARY_CLOUD, SpellHostAreaInd, i40, oAOE, i18, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Sunbeam and burst. Very similar spells and if they are not undead, then + // there should be very little chance of casting it. + // - Won't bother getting nearest undead. + if(SpellHostAreaInd && !GlobalInTimeStop && RangeLongValid && + (GetHasSpell(SPELL_SUNBEAM) || GetHasSpell(SPELL_SUNBURST) || + ItemHostAreaDis == SPELL_SUNBEAM || ItemHostAreaDis == SPELL_SUNBURST)) + { + // 40M range, lagre (5.0 across) area. Relfex saves. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i8, SAVING_THROW_REFLEX); + // Is it valid? 20% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Sunburst. Level 8 (Druid/Mage) 6d6 to non-undead. Kills vampires. Blindness. Limit of 25 dice for undead damage. Medium range/colosal size + if(AI_ActionCastSpellRandom(SPELL_SUNBURST, SpellHostAreaDis, i10, oAOE, i18, TRUE, ItemHostAreaDis)) return TRUE; + // Sunbeam. Level 8 (Cleric/Druid) 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size + if(AI_ActionCastSpellRandom(SPELL_SUNBEAM, SpellHostAreaDis, i10, oAOE, i18, TRUE, ItemHostAreaDis)) return TRUE; + } + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Single spells at end + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i8) + { + // 40% chance + if(!GetHasSpellEffect(SPELL_BIGBYS_CLENCHED_FIST, GlobalSpellTarget) && + !AI_CompareTimeStopStored(SPELL_BIGBYS_CLENCHED_FIST) && + RangeLongValid) + { + // Bigby's Clenched Fist. Level 8 (Mage) Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well. + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CLENCHED_FIST, SpellHostRanged, i30, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE; + } + // No other single spells for level 8. + + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + + // Check if we had any of the above we didn't cast based on the % + if(AI_ActionCastBackupRandomSpell()) return TRUE; + +/*::7777777777777777777777777777777777777777777777777777777777777777777777777777 + Level 7 Spells. +//::7777777777777777777777777777777777777777777777777777777777777777777777777777 + Thoughts - well, quite a varied amount of spells. Among classes, there are + some variety. SoU adds Banishment, and Bigby's. Quite a few death spells + but note: Word of faith is very effective against 11s and under - stun, for + under 4's, death! + + Hordes adds Great Thunderclap - requires 3 saves! :-) + + AOE: +S X [Banishment] - Destroys summons (familiars, creatures, spells) to a 2xCasters HD limit. + [Word of Faith] - Enemies only. 4 Or Down die. 4+ Confuse Stun, Blind. 8+ Stun + Blind. 12+ Only Blind. (1Round/2Casterlevels) outsiders killed. + [Creeping Doom] - Until 1000 damage, d6 + d6/round stayed in damage in an AOE. + [Delayed Fireball Blast] - Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!) + [Prismatic Spray] - Random damage/effects. Chance of doing double amount of effects. +H [Great Thunderclap] - Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown. + + Single: + [Distruction] - Short ranged instant death for fort, else take 10d6 Negative energy. Death + Necromantic. +S [Bigby's Grasping Hand] - Hold target if sucessful grapple + X [Control Undead] - Yep, controls undead! Dominates them. Cast above near sunbeam. + [Finger of Death] - Short ranged instant Death on fort else 3d6 negative energy. + [Power Word, Stun] - Instant stun based on HP of target. Cast above if few targets. Here if more. + + Defender: + [Aura of Vitality] - +4 STR, CON, DEX for all allies. + [Protection From Spells] +8 to all saves. Cast above, backup here. + [Shadow Shield] - Necromancy/negative energy immunity. Some other resistances and some DR. + X [Spell Mantle] - 1d8 + 8 spells stopped. Cast above (This is good VS anything that casts spells) + [Regenerate] - 6HP/Round healed. Cast above if damaged. Cast here anyway :-) + + Summon: + [Summon Monster VII (7)] - Huge elemental + [Mordenkainen's Sword] - Good, nay, very good hitting summon. + + Other: + X [Greater Restoration] - Heals lots of effects. + X [Resurrection] - Resurrection :-) cast on dead people not here. + + Do Palemaster Death touch here. Always use it if they are not immune. +//::77777777777777777777777777777777777777777777777777777777777777777777777777*/ + + // Jump out if we don't want to cast level 7 spells. + if(iLowestSpellLevel > i7) return FALSE; + + // Cast Shadow Shield only first. Good protections really :-) + // Visages are generally lower DR, with some spell-resisting or effect-immunty extras. + + if(IsFirstRunThrough) + { + // Shadow Shield - has 10/+3 damage reduction + lots of stuff. Level 7 (Mage) + if(AI_SpellWrapperVisageProtections(i7)) return TRUE; + + // Protection From Spells. + if(!AI_GetAIHaveSpellsEffect(GlobalHasProtectionSpellsSpell)) + { + // Protection from spells. Level 7 (Mage), for +8 on all saves (Area effect too!) + if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_SPELLS, SpellProAre, OBJECT_SELF, i17, FALSE, ItemProAre)) return TRUE; + } + } + + // Palemaster death touch + // DC 17 + (Pale Master - 10) /2. + if(RangeTouchValid) + { + // Cannot affect creatures over large size + // - Module switch, but always checked here. + if(GetCreatureSize(GlobalSpellTarget) <= CREATURE_SIZE_LARGE && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath) && + // Fort save DC 17 + GlobalSpellTargetFort <= i16) + { + // Use the feat + if(AI_ActionUseFeatOnObject(FEAT_DEATHLESS_MASTER_TOUCH, GlobalSpellTarget)) return TRUE; + } + // Undead graft paralyzes! + if(GlobalSpellTargetRace != RACIAL_TYPE_ELF && + // Fort save - 14 + Palemaster levels / 2 + !GlobalSpellTargetFort < (i14 + GetLevelByClass(CLASS_TYPE_PALEMASTER)/i2)) + { + // Use the feat + if(AI_ActionUseFeatOnObject(FEAT_UNDEAD_GRAFT_1, GlobalSpellTarget)) return TRUE; + // 2 versions + if(AI_ActionUseFeatOnObject(FEAT_UNDEAD_GRAFT_2, GlobalSpellTarget)) return TRUE; + } + } + + // Hostile spells here. + // We randomly choose one to cast (with higher %'s for some!) +// AOE: +// [Word of Faith] - Enemies only. 4 Or Down die. 4+ Confuse Stun, Blind. 8+ Stun + Blind. 12+ Only Blind. (1Round/2Casterlevels) outsiders killed. +// [Creeping Doom] - Until 1000 damage, d6 + d6/round stayed in damage in an AOE. +// [Delayed Fireball Blast] - Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!) +// [Prismatic Spray] - Random damage/effects. Chance of doing double amount of effects. +// [Great Thunderclap] - Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown. +// [Stonehold] - AOE - Will Encase people in stone (Paralysis) VS Will Mind throw. (Save each round) +// Single: +// [Distruction] - Short ranged instant death for fort, else take 10d6 Negative energy. Death + Necromantic. +// [Bigby's Grasping Hand] - Hold target if sucessful grapple +// [Finger of Death] - Short ranged instant Death on fort else 3d6 negative energy. +// [Power Word, Stun] - Instant stun based on HP of target. Cast above if few targets. Here if more. + + // Is it best to target with single-target spells first? Most are pretty good :-D + // 60-70% if favourable. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i7) + { + // Killing spells. These, if sucessful, rend all status-effect spells redundant. + // - Only fired if they are not immune :-D + if(RangeShortValid && !AI_CompareTimeStopStored(SPELL_DESTRUCTION, SPELL_FINGER_OF_DEATH) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i7) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath)) + { + // Destruction, level 7 (Cleric). Fort (Death) for Death if fail, or 10d6 damage if pass. + if(AI_ActionCastSpellRandom(SPELL_DESTRUCTION, SpellHostRanged, i60, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE; + // Finger of Death. Level 7 (Mage). Fort (Death) for death if fail, or d6(3) + nCasterLvl damage if pass. + if(AI_ActionCastSpellRandom(SPELL_FINGER_OF_DEATH, SpellHostRanged, i50, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE; + } + // Is not immune to mind spell (I think this is a valid check) and not already stunned. + // Really, if under < 151 HP to be affected - short ranged + if (GlobalSpellTargetCurrentHitPoints <= i150 && RangeShortValid && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_CompareTimeStopStored(SPELL_POWER_WORD_STUN)) + { + // Power Word Stun. Level 7 (Wizard). Stun duration based on HP. + if(AI_ActionCastSpellRandom(SPELL_POWER_WORD_STUN, SpellHostAreaInd, i60, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE; + } + // Bigbiy's Grasping hand last. We don't bother checking for grapple checks - + // they mainly work anyway, if anything. Powerful in its own right, as + // it holds on a sucessful check. + if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_GRASPING_HAND, GlobalSpellTarget) && + !AI_CompareTimeStopStored(SPELL_BIGBYS_GRASPING_HAND) && + !AI_GetSpellTargetImmunity(GlobalImmunityStun)) + { + // Bigby's Grasping Hand. Level 7 (Mage) Hold target if sucessful grapple + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_GRASPING_HAND, SpellHostRanged, i40, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE; + } + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + + // AOE spells now. +// [Word of Faith] - Enemies only. 4 Or Down die. 4+ Confuse Stun, Blind. 8+ Stun + Blind. 12+ Only Blind. (1Round/2Casterlevels) outsiders killed. +// [Creeping Doom] - Until 1000 damage, d6 + d6/round stayed in damage in an AOE. +// [Delayed Fireball Blast] - Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!) +// [Prismatic Spray] - Random damage/effects. Chance of doing double amount of effects. +// [Great Thunderclap] - Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown. + + // Word of faith + // Doesn't hurt allies, will-based save, at the very least blindness :-D Medium range, collosal size + if(SpellHostAreaDis && RangeMediumValid && + (GetHasSpell(SPELL_WORD_OF_FAITH) || ItemHostAreaDis == SPELL_WORD_OF_FAITH)) + { + // 20M medium range, colossal size, will save to deflect. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_COLOSSAL, i7, SAVING_THROW_WILL); + // Is it valid? 70% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Word of Faith. Level 7 (Cleric) Enemies only affected. 4 Or Down die. 4+ [Confuse|Stun|Blind]. 8+ [Stun|Blind]. 12+ [Blind]. (1Round/2Casterlevels) outsiders killed. + if(AI_ActionCastSpellRandom(SPELL_WORD_OF_FAITH, SpellHostAreaDis, i60, oAOE, i17, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Creeping Doom + // Damage to all in AOE, to 1000 damage. d6(rounds in it) basically. Good AOE spell. + // Only 50% chance of casting, to not cast too many. + if(SpellHostAreaDis && RangeMediumValid && !GlobalInTimeStop && + (GetHasSpell(SPELL_CREEPING_DOOM) || ItemHostAreaDis == SPELL_CREEPING_DOOM)) + { + // 20M medium range, we'll say a huge size. No save can stop all damage ;-) + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i7, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Creeping Doom. Level 7 (Druid) Until 1000 damage, d6 + d6/round stayed in damage in an AOE. + if(AI_ActionCastSpellRandom(SPELL_CREEPING_DOOM, SpellHostAreaDis, i40, oAOE, i17, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Delayed Fireball Blast. Big fireball. Lots of fire damage - reflex saves. + if(SpellHostAreaInd && RangeMediumValid && + !AI_CompareTimeStopStored(SPELL_DELAYED_BLAST_FIREBALL, SPELL_FIREBALL) && + (GetHasSpell(SPELL_DELAYED_BLAST_FIREBALL) || ItemHostAreaInd == SPELL_DELAYED_BLAST_FIREBALL)) + { + // 20M medium range, blast is RADIUS_SIZE_HUGE, lower then Fireball, but more deadly (both save + damage) + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i7, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Delayed Fireball Blast. Level 7 (Mage) Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!) + if(AI_ActionCastSpellRandom(SPELL_DELAYED_BLAST_FIREBALL, SpellHostAreaInd, i50, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Great Thunderclap is OK, but doesn't really do too much. If anything, the + // 3 saves are cool :-P + if(SpellHostAreaInd && RangeMediumValid && + !AI_CompareTimeStopStored(SPELL_GREAT_THUNDERCLAP) && + (GetHasSpell(SPELL_GREAT_THUNDERCLAP) || ItemHostAreaInd == SPELL_GREAT_THUNDERCLAP)) + { + // 20M medium range, hits a gargantuan area. + // - We ignore saves for this. + // - Doesn't actually hit ourselves. Won't bother checking this though. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_GARGANTUAN, i7, SAVING_THROW_ALL, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Great Thunderclap. Level 7 (Mage) Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown. + if(AI_ActionCastSpellRandom(SPELL_GREAT_THUNDERCLAP, SpellHostAreaInd, i40, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Lastly, AOE wise, it is prismatic spray. Comparable, if a little (or very!) + // erratic. We might as well cast this whatever (So if they all are immune + // to the delay fireballs we have, this does good damage compared to 0!) + if(SpellHostAreaInd && RangeShortValid && + !AI_CompareTimeStopStored(SPELL_PRISMATIC_SPRAY) && + (GetHasSpell(SPELL_PRISMATIC_SPRAY) || ItemHostAreaInd == SPELL_PRISMATIC_SPRAY)) + { + // 8M short range, blast is a cone, and no save. Spell script has fSpread at 11.0 + oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, i7, FALSE, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly); + // Is it valid? 40% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Prismatic Spray. Level 7 (Mage) Random damage/effects. Chance of doing double amount of effects. + if(AI_ActionCastSpellRandom(SPELL_PRISMATIC_SPRAY, SpellHostAreaInd, i30, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Level 7 summon spells. Also cast the unique class-based summons (like + // a blackguards undead, a shadowdancers shadow ETC). + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i7) + { + // Always use our feat-based ones (not level dependant) as they increase + // with level. + // - Shadow based on level + if(AI_ActionCastSummonSpell(FEAT_SUMMON_SHADOW, iM1, i7)) return TRUE; + // - Undead warrior (EG: doomknight) based on level + if(AI_ActionCastSummonSpell(AI_FEAT_BG_CREATE_UNDEAD, iM1, i7)) return TRUE; + // - Shadow based on level + if(AI_ActionCastSummonSpell(AI_FEAT_BG_FIENDISH_SERVANT, iM1, i7)) return TRUE; + // Pale master + if(AI_ActionCastSummonSpell(AI_FEAT_PM_CREATE_UNDEAD, iM1, i7)) return TRUE; + + // Then, the normal summons. + if(GlobalOurHitDice <= i18 || GlobalMeleeAttackers <= i2) + { + // Mordenkainen's Sword Level 7 (Mage). Good, nay, very good hitting summon. + if(AI_ActionCastSummonSpell(SPELL_MORDENKAINENS_SWORD, i17, i7)) return TRUE; + + // Summon Monster VII (7). Level 7 (Cleric, Mage, Druid, etc) Huge elemental + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VII, i17, i7)) return TRUE; + } + } + + // Now, back to single spell targets again. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i7) + { + // Killing spells. These, if sucessful, rend all status-effect spells redundant. + // - Only fired if they are not immune :-D + if(RangeShortValid && !AI_CompareTimeStopStored(SPELL_DESTRUCTION, SPELL_FINGER_OF_DEATH) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i7) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath)) + { + // Destruction, level 7 (Cleric). Fort (Death) for Death if fail, or 10d6 damage if pass. + if(AI_ActionCastSpellRandom(SPELL_DESTRUCTION, SpellHostRanged, i30, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE; + // Finger of Death. Leve 7 (Mage). Fort (Death) for death if fail, or d6(3) + nCasterLvl damage if pass. + if(AI_ActionCastSpellRandom(SPELL_FINGER_OF_DEATH, SpellHostRanged, i20, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE; + } + // Bigbiy's Grasping hand last. We don't bother checking for grapple checks - + // they mainly work anyway, if anything. Powerful in its own right, as + // it holds on a sucessful check. + if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_GRASPING_HAND, GlobalSpellTarget) && + !AI_CompareTimeStopStored(SPELL_BIGBYS_GRASPING_HAND) && + !AI_GetSpellTargetImmunity(GlobalImmunityStun)) + { + // Bigby's Grasping Hand. Level 7 (Mage) Hold target if sucessful grapple + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_GRASPING_HAND, SpellHostRanged, i20, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE; + } + // Is not immune to mind spell (I think this is a valid check) and not already stunned. + // Really, if under < 151 HP to be affected - short ranged + if (GlobalSpellTargetCurrentHitPoints <= i150 && RangeShortValid && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_CompareTimeStopStored(SPELL_POWER_WORD_STUN)) + { + // Power Word Stun. Level 7 (Wizard). Stun duration based on HP. + if(AI_ActionCastSpellRandom(SPELL_POWER_WORD_STUN, SpellHostAreaInd, i20, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE; + } + } + + // Backup casting of the level 7 hostle spells, just below the last + // level 7 summon spells :-) + if(AI_ActionCastBackupRandomSpell()) return TRUE; + +/*::6666666666666666666666666666666666666666666666666666666666666666666666666666 + Level 6 Spells. +//::6666666666666666666666666666666666666666666666666666666666666666666666666666 + Thoughts - a great variety of spells here, from defenses like Greater Stoneskin, + down to versitile Shades, Flesh to stone and chain lightning. For all classes, + level 6 spells give the greatest variety. + + Hordes adds a few, as does SoU. + + AOE: + [Acid Fog] - Acid damage in an AOE, including slow in AOE. 1d6/round in fog. + [Chain Lightning] - Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage. + X [Circle of Death] - Under 9HD people save or die. 1d4 creatures/caster leve. Cast above +S [Isaac's Greater Missile Storm] - !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save! + [Blade Barrier] - Lots of spikes = Lots of damage. Piercing, relfex saves. +H X [Undead to Death] - Slays 1d4HD worth of undead/level. (Max 20d4). Lowest first. + + Cast as single target: + +H [Evil Blight] - All in a collosal area have a curse (-3 stats) on them. Will save. + + Mobile AOE: + +S [Dirge] - Continual Strength Damage to those in the AOE (Mobile, on self) + + [Greater Dispeling] - Dispels all effects on 1 target, or 1 on an area of targets. (to 15 caster levels) + [Greater Spell Breach] - Strips 6 protection spells, then lowers SR of target. + + Single: +S [Bigby's Forceful Hand] - Bullrush to make the target Knockdowned and Dazed all in one. +S [Flesh to Stone] - Petrifies a target on a fortitude save. + [Harm] - 1d4 HP left on target, if touch attacked. +S [Drown] - Up to 90% of current HP damage done. Fort save for none. +H X [Crumble] - 1d6/level damage (to 15d6) only to constructs. + + Defender: + [Ethereal Visage] - 25% consealment. 20/+3 DR. 0/1/2 spells immune to. + [Globe of Invulnerability] - Immunity to 4, 3, 2, 1, 0 level spells. + [Greater Stoneskin] - 20/+5 DR. We cast this here, if we have not got it, to prepare for Premonition going down. + X [Mass Haste] - Haste for allies in area. + + Summon: + [Summon Monster VI (6)] - Dire Tiger + [Planar Binding] - Summons Subbucus (Evil), Hound Arcon (Good), Green Slaad (Neutral). AI won't target outsiders specifically. + [Create Undead] - Creates a lowish undead to aid the caster. + [Planar Ally] - Waoh! Same as planar binding! Exactly! :-) (except no stopping outsiders) + + Other: +XXXX[Legend Lore] - Lots of lore. Never cast. + X [Shades] - Offense and defense spells. NPC's can cast these right now. Cast same time as normal versions + X [Stone to Flesh] - Un-petrifies a target. + X [Tenser's Transformation] - Uses polymorph, so cast after spells. Massive HP boost and BAB boost. + X [True Seeing] - See invisible creatures/hidden ones (thats a bug) meant to stop illusions. Cast in special cases. + X [Heal] - Heals all damage/harms undead. + X [Healing Circle] - Heals damage as critcal wounds in an AOE. + + Note that there are some monster abilities - the howl attacks, here. + Sorta like pulses, 80% chance to check them, that sort of thing :-D + + We also cast visage protections here. Ghoslty, no, not until level 3 spells. + + We also cast, as a starter, elemental protections :-) + + We check if we can cast Golem Ranged Slam here too. + + Trying to do dragon diciple breath too! :-) +//::66666666666666666666666666666666666666666666666666666666666666666666666666*/ + + // Jump out if we don't want to cast level 6 spells. + if(iLowestSpellLevel > i6) return FALSE; + + if(IsFirstRunThrough) + { + // Best elemental protection (maybe % chance here...and later do it 100%) + if(AI_SpellWrapperElementalProtections(iLowestSpellLevel)) return TRUE; + + // Visage - eathereal :-) + // Ethereal Visage. Level 6 (Mage) 25% consealment. 20/+3 DR. 0/1/2 spells immune to. + if(AI_SpellWrapperVisageProtections(i6)) return TRUE; + + // Globe of Invunrability. We cast minor globe if we have under 10HD (Else we'll try + // it lower down anyway) + if(GlobalOurHitDice <= i10) + { + if(AI_SpellWrapperGlobeProtections(iLowestSpellLevel)) return TRUE; + } + else + { + if(AI_SpellWrapperGlobeProtections(i6)) return TRUE; + } + + // Cast Greater Stoneskin if not got the specific greater stoneskin spell + // because we want to prepare for premonition going down, and we must have + // checked all level 7, 8 and 9 spells anyway + if(!GetHasSpellEffect(SPELL_GREATER_STONESKIN)) + { + // Then, greater stoneskin protects a lot of damage - + // Greater Stoneskin. Level 6 (Mage) 7 (druid) 20/+5 phiscial damage reduction + if(AI_ActionCastSpell(SPELL_GREATER_STONESKIN, SpellProSelf, OBJECT_SELF, i16, FALSE, ItemProSelf, PotionPro)) return TRUE; + } + } + + // Dispel level 4 protections (Hastes, regenerates, tensors...) + if(RangeMediumValid && !GlobalInTimeStop)// All medium spells. + { + // Dispel number need to be 4 for breach + if(GlobalDispelTargetHighestBreach >= i4) + { + // Wrapers Greater and Lesser Breach. + if(AI_ActionCastBreach()) return TRUE; + } + // Dispel >= 4 + if(GlobalDispelTargetHighestDispel >= i4) + { + // Wrappers the dispel spells + if(AI_ActionCastDispel()) return TRUE; + } + } + + if(IsFirstRunThrough) + { + // Cast more buff spells + // Buffing spells (energy buffer and downwards) + // 40% chance. + if(AI_ActionCastAllyBuffSpell(f6, i40, SPELL_ENERGY_BUFFER, SPELL_PROTECTION_FROM_ELEMENTS, SPELL_RESIST_ELEMENTS, SPELL_ENDURE_ELEMENTS)) return TRUE; + } + + // Dirge. + // - Cast always, basically. Boring :-) but its an OK spell + if(IsFirstRunThrough && !GetHasSpellEffect(SPELL_DIRGE)) + { + // Dirge. Level 6 (Bard). Continual Strength Damage to those in the AOE (Mobile, on self) + if(AI_ActionCastSpell(SPELL_DIRGE, SpellHostAreaInd, GlobalSpellTarget, i15, FALSE, ItemHostAreaInd)) return TRUE; + } + + // Golem ranged slam. 80% chance of using. + if(RangeLongValid && GlobalSeenSpell && d10() <= i8) + { + // Golem Ranged Slam. Long ranged, Random(30) + 30 Blud damage. Can do knockdown too. + if(AI_ActionCastSpell(AI_SPELLABILITY_GOLEM_RANGED_SLAM, SpellHostRanged, GlobalSpellTarget)) return TRUE; + } + + // Dragon Diciple breath. + if(RangeTouchValid && + // Reflex save. 19 + 1 per 4 levels after 10. + GlobalSpellTargetReflex < (i19 + (GetLevelByClass(CLASS_TYPE_DRAGONDISCIPLE) - i10)/i4)) + { + // Dragon diciple breath - x2_s2_descbreath + if(AI_ActionUseFeatOnObject(FEAT_DRAGON_DIS_BREATH, GlobalSpellTarget)) return TRUE; + } + + // Howl! HOOOOOOOOWWWWWWWWWWWLLLLLLLLL! Collosal range on self. + // Most are decent enough to cast as level 6 spells, centred on self, 80% chance to cast. + // We also randomly choose one (and always cast one if we can cast one :-) ) + if(RangeTouchValid && GlobalSeenSpell && d10() <= i8) + { + // We cast all of these, but randomly. It works through with most powerful + // getting the highest %'s of course :-) + if(!AI_GetSpellTargetImmunity(GlobalImmunityDeath)) + { + // 50% chance of death howl. + if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_DEATH, SpellHostAreaInd, i40)) return TRUE; + } + // Sonic damage is powerful - 40% + // Fortitude save or sonic damage d6(HD/4) :-) + if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_SONIC, SpellHostAreaInd, i30)) return TRUE; + + // Can't be immune to mind for most of the rest + if(!AI_GetSpellTargetImmunity(GlobalImmunityMind)) + { + // Mind blast + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_MINDFLAYER_MINDBLAST_10, SpellHostAreaInd, i20)) return TRUE; + // Other one + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_MINDFLAYER_PARAGON_MINDBLAST, SpellHostAreaInd, i20)) return TRUE; + // Other one 2 + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_PSIONIC_MASS_CONCUSSION, SpellHostAreaInd, i20)) return TRUE; + + // Fear howl. 40% chance + if(!AI_GetSpellTargetImmunity(GlobalImmunityFear)) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_FEAR, SpellHostAreaInd, i20)) return TRUE; + } + // Paralisis and daze (mind effects, and stunning ones) + if(!AI_GetSpellTargetImmunity(GlobalImmunityStun)) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_PARALYSIS, SpellHostAreaInd, i20)) return TRUE; + if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_DAZE, SpellHostAreaInd, i20)) return TRUE; + } + } + // Doom last. -X's amounts of stats. Also note, don't cast if already + // affected :-) + if(!GetHasSpellEffect(SPELLABILITY_HOWL_DOOM)) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_DOOM, SpellHostAreaInd, i20)) return TRUE; + } + + // Harpy song. + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_HARPYSONG, SpellHostAreaInd, i20)) return TRUE; + + // Finally, if we got the 80% chance of using one, and none of them + // random cast, backup with this. + if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + + // Now, randomish attack spells we possess. +// AOE: +// [Acid Fog] - Acid damage in an AOE, including slow in AOE. 1d6/round in fog. +// [Chain Lightning] - Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage. +// [Isaac's Greater Missile Storm] - !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save! +// [Blade Barrier] - Lots of spikes = Lots of damage. Piercing, relfex saves. + +// Single: +// [Bigby's Forceful Hand] - Bullrush to make the target Knockdowned and Dazed all in one. +// [Flesh to Stone] - Petrifies a target on a fortitude save. +// [Harm] - 1d4 HP left on target, if touch attacked. +// [Drown] Up to 90% of current HP damage done. Fort save for none. +// [Evil Blight] - All in a collosal area have a curse (-3 stats) on them. Will save. + + // Is it best to target with single-target spells first? Flesh to stone + // alone makes single target level 6 spells decent enough :-) + // 60-70% if favourable. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i6) + { + // Drown! We don't do this if they are under 30HP already. + // 80% chance to cast if we have it, and not immune to the save. + if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_DROWN) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i6) && + GlobalSpellTargetCurrentHitPoints >= i30) + { + // Drown. level 6 (Druid) Up to 90% of current HP damage done. Fort save for none. + if(AI_ActionCastSpellRandom(SPELL_DROWN, SpellHostRanged, i70, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + // Flesh to Stone is a good spell - petrify attack. + // 70% chance to cast if we have it, and not immune to the save. + if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_FLESH_TO_STONE) && + !AI_GetSpellTargetImmunity(GlobalImmunityPetrify) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i6)) + { + // Flesh to Stone. - Petrifies a target on a fortitude save. + if(AI_ActionCastSpellRandom(SPELL_FLESH_TO_STONE, SpellHostRanged, i60, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + // Evil Blight. This is an AOE curse, but note that we cannot check + // if an AOE already has it. + if(RangeMediumValid && !AI_CompareTimeStopStored(AI_SPELL_EVIL_BLIGHT) && + !AI_GetSpellTargetImmunity(GlobalImmunityCurse) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i6)) + { + // Evil Blight. Level 6 (Mage) -3 Curse to all in AOE (Will save) + if(AI_ActionCastSpellRandom(AI_SPELL_EVIL_BLIGHT, SpellHostTouch, i60, GlobalSpellTarget, i16, FALSE, ItemHostTouch)) return TRUE; + } + // Bigby's Forceful hand. No need to check for bullrush attack. It + // will knockdown and daze a target :-) Quite powerful as no save. + // (Count as stun for immunity - that is anyting that stops them moving (daze included)) + // - Should affect mind-immune people. Bioware will fix this, been told. No mind check + if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_FORCEFUL_HAND, GlobalSpellTarget) && + !AI_CompareTimeStopStored(SPELL_BIGBYS_FORCEFUL_HAND) && + !AI_GetSpellTargetImmunity(GlobalImmunityStun)) + { + // Bigby's Forceful Hand. Level 6 (Mage) Bullrush to make the target Knockdowned and Dazed all in one. + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_FORCEFUL_HAND, SpellHostRanged, i30, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + // AOE spells - including the now-infamous Isaac's Greater Missile Storm! +// [Acid Fog] - Acid damage in an AOE, including slow in AOE. 1d6/round in fog. +// [Chain Lightning] - Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage. +// [Isaac's Greater Missile Storm] - !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save! +// [Blade Barrier] - Lots of spikes = Lots of damage. Piercing, relfex saves. + + // Isaac's Greater Missile Storm - only hits enemies, targeted on an area + // around the caster, lots of missiles which do 2d6 MAGICAL energy each!!! + // Ok, this is sad, but because it goes well targeting many, or one target, + // we will target this at the spell target regardless of range (which is + // long anyway!) or better (more in AOE) targets. 80% chance of casting. + // Cast at ground. + + if(RangeLongValid && GlobalNormalSpellsNoEffectLevel < i6) + { + // Isaac's Greater Missile Storm. Level 6 (Mage) !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save! + if(AI_ActionCastSpellRandom(SPELL_ISAACS_GREATER_MISSILE_STORM, SpellHostRanged, i70, GlobalSpellTarget, i16, TRUE, ItemHostRanged)) return TRUE; + } + + // Chain lightning is a decent-damage, no, good-damage spell, like fireball, + // but a better way - it damages all enemies only :-D + // Because it damages enemies only, we aim it at the current spell target, + // because it needs a target. + if(RangeLongValid && !AI_CompareTimeStopStored(SPELL_CHAIN_LIGHTNING) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i6)) + { + // Collosal area, long range, reflex throw. 60% chance of casting. + // Chain Lightning. Level 6 (Mage). Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage. + if(AI_ActionCastSpellRandom(SPELL_CHAIN_LIGHTNING, SpellHostAreaDis, i50, GlobalSpellTarget, i16, FALSE, ItemHostAreaDis)) return TRUE; + } + // Blade Barrier - lots of damage, up to 20d6 damage to targets :-) + // Wierd shape, however. It is 2M across one way, but 10M long, retangle. + // We just target a large (5.0) area, and as long as it hits an enemy object, + // it is great! + if(SpellHostAreaInd && RangeMediumValid && + !AI_CompareTimeStopStored(SPELL_BLADE_BARRIER) && + (GetHasSpell(SPELL_BLADE_BARRIER) || SpellHostAreaInd == SPELL_BLADE_BARRIER)) + { + // 20M medium range, large area we'll say. Reflex save + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i6, SAVING_THROW_REFLEX); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Blade Barrier. Level 6 (Cleric) Lots of spikes = Lots of damage (to 20d6). Piercing, relfex saves. + if(AI_ActionCastSpellRandom(SPELL_BLADE_BARRIER, SpellHostAreaInd, i40, oAOE, i16, TRUE, ItemHostAreaInd)) return TRUE; + } + } + + // Level 6 summons + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i6 && + (GlobalOurHitDice <= i16 || GlobalMeleeAttackers <= i2)) + { + // Summon Monster VI (6). Level 6 (Most classes) Dire Tiger + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VI, i16, i6)) return TRUE; + // Planar Binding. Level 6 (Mage). Summons Subbucus (Evil), Hound Arcon (Good), Green Slaad (Neutral). AI won't target outsiders specifically. + if(AI_ActionCastSummonSpell(SPELL_PLANAR_BINDING, i16, i6)) return TRUE; + // Create Undead. Level 6 (Cleric) 8 (Mage). Creates a lowish undead to aid the caster. + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VI, i16, i6)) return TRUE; + // Planar Ally. Level 6 (Cleric) - Waoh! Same as planar binding! Exactly! :-) (except no stopping outsiders) + if(AI_ActionCastSummonSpell(SPELL_PLANAR_ALLY, i16, i6)) return TRUE; + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Lastly, the single-target spells again :-) + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i6) + { + // Flesh to Stone is a good spell - petrify attack. + // 50% chance to cast if we have it, and not immune to the save. + if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_FLESH_TO_STONE) && + !AI_GetSpellTargetImmunity(GlobalImmunityPetrify) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i6)) + { + // Flesh to Stone. - Petrifies a target on a fortitude save. + if(AI_ActionCastSpellRandom(SPELL_FLESH_TO_STONE, SpellHostRanged, i40, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + // Evil Blight. This is an AOE curse, but note that we cannot check + // if an AOE already has it. + if(RangeMediumValid && !AI_CompareTimeStopStored(AI_SPELL_EVIL_BLIGHT) && + !AI_GetSpellTargetImmunity(GlobalImmunityCurse) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i6)) + { + // Evil Blight. Level 6 (Mage) -3 Curse to all in AOE (Will save) + if(AI_ActionCastSpellRandom(AI_SPELL_EVIL_BLIGHT, SpellHostTouch, i30, GlobalSpellTarget, i16, FALSE, ItemHostTouch)) return TRUE; + } + // Bigby's Forceful hand. No need to check for bullrush attack. It + // will knockdown and daze a target :-) Quite powerful as no save. + // (Count as stun for immunity - that is anyting that stops them moving (daze included)) + // - Should affect mind-immune people. Bioware will fix this, been told. No mind check + if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_FORCEFUL_HAND, GlobalSpellTarget) && + !AI_CompareTimeStopStored(SPELL_BIGBYS_FORCEFUL_HAND) && + !AI_GetSpellTargetImmunity(GlobalImmunityStun)) + { + // Bigby's Forceful Hand. Level 6 (Mage) Bullrush to make the target Knockdowned and Dazed all in one. + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_FORCEFUL_HAND, SpellHostRanged, i20, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + } + + // % casting recast again, before next set + if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Acid fog - slow, and damage in acid fog. + // Decent..but ...well, its alright :-) + // THIS is spell 0 spell :-) we cast 100% and no backup casting. + if(RangeLongValid && SpellHostAreaInd && !GlobalInTimeStop && + (GetHasSpell(SPELL_ACID_FOG) || SpellHostAreaInd == SPELL_ACID_FOG)) + { + // 40M spell range, 5M radius (large) fortitude save, but doesn't stop damage. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i6); + // Is it valid? 40% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Acid Fog. Level 6 (Mage) Acid damage in an AOE, including slow in AOE. 1d6/round in fog. + if(AI_ActionCastSpell(SPELL_ACID_FOG, SpellHostAreaInd, oAOE, i16, TRUE, ItemHostAreaInd)) return TRUE; + } + } + +/*::5555555555555555555555555555555555555555555555555555555555555555555555555555 + Level 5 Spells. +//::5555555555555555555555555555555555555555555555555555555555555555555555555555 + Thoughts - A good variety of AOE spells, but lacking good single target spells + generally. Of course, some spells are cast at different levels :-P . Slay + living is probably the lowest Save-Or-Die spell, while the rest give decent + damage (AOE spells that is). Some specilist spells include Mind Fog and + Feeble Mind :-) + + AOE: + [Cloudkill] - Acid damage, and kills level 7s or below. AOE. + [Cone of Cold] - Cone of damage, up to 15d6 to those in the cone. Reflex for none. + X [Dismissal] - Destroys summons in AOE against a will save + 6DC. Cast above +S [Firebrand] - Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage. + [Mind Fog] - Minus 10 to all will saves within the AOE. + [Circle of Doom] - 1d8 + 1/caster level in negative damage + [Flame Strike] - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area. +H [Ball Lightning] - 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms) +HXXX[Vine mine] - Entangle, 50% movement, or Camoflage. Note: Not cast ever! Stupid spell! + + [Battletide] - +2 Save/Attack/Damage to cast. -2 to enemies who enter. Mobile AOE on self. + + Single: +S [Bigby's Interposing Hand] -10 to hit for 1 target. + X [Dominate Person] - Dominates 1 humanoid person. Cast above + [Feeblemind] - 1d4/4 caster levels in intelligence decrease, if fail will save. + [Hold Monster] - Paralyze's an enemy monster (any race) + [Slay Living] - Touch attack + Fortitude Save, creature dies, else d6(3) negative damage. +H [Inferno] - 2d6 Fire damage/round like acid arrow. No save, only SR. + + Defender: + [Energy Buffer] - 40/- elemental resistance to Cold, Acid, Fire, Sonic and electicity, to 60 damage total. + X [Lesser Spell Mantle] - 1d4 + 6 spells immune to. Cast above + [Spell Resistance] - 12 + Caster Level in spell resistance. +H X [Mestil's Acid Sheath] - 1d6 + 2/level in acid damage to attackers. + + Summon: + [Summon Creature V (5)] - Dire Bear + [Lesser Planar Binding] - Imp (Evil), Slaad Red (Neutral), Lantern Archon (Good) + + Other: + [Greater Shadow Conjuration] - Variety of spells, illision based. + [Lesser Mind Blank] - Protection VS mind spells and rids bad mind things. + [Raise Dead] - Raises a dead person :-) + [Awaken] - Helps animal companion greatly. +S [Owl's Insight] - + Half caster level in wisdom. +H [Monsterous Regeneration] - +3 Regeneration for CasterLevel/2 + 1. + + We check level 3 dispels here. + + Resistance to fire ETc (energy buffer, elemental resistances) are cast + in the level 6 set. + + Cones breath things too here. + + Giant Hurl Rocks are also here (and some other hurling things) +//::55555555555555555555555555555555555555555555555555555555555555555555555555*/ + + // Jump out if we don't want to cast level 5 spells. + if(iLowestSpellLevel > i5) return FALSE; + + // Monsterous Regeneration if we have 70% or less HP + if(IsFirstRunThrough && GlobalOurPercentHP <= i70 && + !GetHasSpellEffect(SPELL_MONSTROUS_REGENERATION)) + { + // Monsterous Regeneration. Level 5 (Cleric/Druid) +3 Regeneration for CasterLevel/2 + 1. + if(AI_ActionCastSpell(SPELL_MONSTROUS_REGENERATION, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar)) return TRUE; + } + + // Spell reistance - feel the vibes :-) Cast above also if mages around, or need spell protections. + if(IsFirstRunThrough && !AI_GetAIHaveSpellsEffect(GlobalHasSpellResistanceSpell)) + { + // Spell Resistance. Level 5 (Druid/Cleric) 12 + Caster Level in spell resistance. + if(AI_ActionCastSpell(SPELL_SPELL_RESISTANCE, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar)) return TRUE; + } + + // Dispel level 3 protections (Hastes, regenerates, tensors...) + if(RangeMediumValid && !GlobalInTimeStop)// All medium spells. + { + // Dispel number need to be 3 for breach + if(GlobalDispelTargetHighestBreach >= i3) + { + // Wrapers Greater and Lesser Breach. + if(AI_ActionCastBreach()) return TRUE; + } + // Dispel >= 3 + if(GlobalDispelTargetHighestDispel >= i3) + { + // Wrappers the dispel spells + if(AI_ActionCastDispel()) return TRUE; + } + } + + // Battletide is not bad. Level 5 (Cleric) + // - Oddly classed under SpellHostAreaDis + if(IsFirstRunThrough && !GetHasSpellEffect(SPELL_BATTLETIDE)) + { + // Battletide. Level 5 (Cleric). -2 Attack/Saves/damage to enemies who come in. +2 to same for caster. + if(AI_ActionCastSpell(SPELL_BATTLETIDE, SpellHostAreaDis, GlobalSpellTarget, i15, FALSE, ItemHostAreaDis)) return TRUE; + } + + // Giant Hurl Rock + if(RangeLongValid && GlobalSeenSpell) + { + // Giant Hurl Rock is for d6(HD/5) + Str. Damage. Huge AOE and bludgeoning damage. + if(AI_ActionCastSpell(AI_SPELLABILITY_GIANT_HURL_ROCK, SpellHostRanged, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + + // Battle Boulder Toss - from Campaign, but is an ability too. d6(3)+5 Bud dam. + if(AI_ActionCastSpell(AI_SPELLABILITY_BATTLE_BOULDER_TOSS, SpellHostRanged, GlobalSpellTarget, FALSE, TRUE)) return TRUE; + } + + // Monster cones - these ignore the GlobalNormalSpellsNoEffectLevel toggle. + if(RangeShortValid) + { + // Small-distance, cone-based spell. + // - Take it as no level, and no save. These scale up with the HD of the + // monster, so on average should be used all the time. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly); + // Is it valid? 50% for each. + if(GetIsObjectValid(oAOE)) + { + // Cones + // Uses the AOE object for cone of cold. + // These are the "Cones". Appropriate to put it here, don'tca think? + for(iCnt = SPELLABILITY_CONE_ACID; iCnt <= SPELLABILITY_CONE_SONIC; iCnt++) + { + if(AI_ActionCastSpellRandom(iCnt, SpellHostAreaInd, i40, oAOE, FALSE, TRUE)) return TRUE; + } + if(AI_ActionCastSpellRandom(SPELLABILITY_HELL_HOUND_FIREBREATH, SpellHostAreaInd, i40, oAOE, FALSE, TRUE)) return TRUE; + } + } + + + // Mind fog cast here, lower prioritory, if the spell target has high will. + // Long range. + if(RangeLongValid && (GlobalSpellTargetWill / i2 >= GlobalSpellAbilityModifier)) + { + // Mind Fog. Level 5 (Mage/Bard) - Minus 10 to all will saves within the AOE. + if(AI_ActionCastSpell(SPELL_MIND_BLANK, SpellHostAreaInd, GlobalSpellTarget, i15, TRUE, ItemHostAreaInd)) return TRUE; + } + + // Feeblemind is Good if in medium range, and is a mage :-) + if(RangeMediumValid && GlobalSeenSpell && + GetLevelByClass(CLASS_TYPE_WIZARD, GlobalSpellTarget) >= GlobalOurHitDice/i4) + { + // Feeblemind. Level 5 (Mage) 1d4/4 caster levels in intelligence decrease, if fail will save. + if(AI_ActionCastSpell(SPELL_FEEBLEMIND, SpellHostRanged, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Randomise one of the many level 5 spells. +// AOE: +// [Cloudkill] - Acid damage, and kills level 7s or below. AOE. +// [Cone of Cold] - Cone of damage, up to 15d6 to those in the cone. Reflex for none. +// [Firebrand] - Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage. +// [Circle of Doom] - 1d8 + 1/caster level in negative damage +// [Flame Strike] - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area. +// [Ball Lightning] - 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms) +// Single: +// [Bigby's Interposing Hand] -10 to hit for 1 target. +// [Hold Monster] - Paralyze's an enemy monster (any race) +// [Slay Living] - Touch attack + Fortitude Save, creature dies, else d6(3) negative damage. +// [Inferno] - 2d6 Fire damage/round like acid arrow. No save, only SR. + + // Is it best to target with single-target spells first? Some pretty + // good level 5 spells - Hold monster and Slay Living are useful :-) + // 60-70% if favourable. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i5) + { + // Slay living. 60% chance to cast - it is a touch spell. + if(RangeTouchValid && !AI_CompareTimeStopStored(SPELL_SLAY_LIVING) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i5)) + { + // Slay Living. Level 5 (Cleric). Touch attack + Fortitude Save, creature dies, else d6(3) negative damage. + if(AI_ActionCastSpellRandom(SPELL_SLAY_LIVING, SpellHostRanged, i50, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Hold monster - Paralyze's an enemy monster (any race) + // Decent enough, if not stunned already - 60% + if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_HOLD_MONSTER) && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i5)) + { + // Hold monster - Paralyze's an enemy monster (any race) + if(AI_ActionCastSpellRandom(SPELL_HOLD_MONSTER, SpellHostRanged, i50, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Inferno - the Druids Acid Arrow (more damage, however, each round). + // ---- No save!! + if(RangeShortValid && !GetHasSpellEffect(SPELL_INFERNO)) + { + // Inferno. Level 5 (Druid) 2d6 Fire damage/round like acid arrow. No save, only SR. + if(AI_ActionCastSpellRandom(SPELL_INFERNO, SpellHostRanged, i50, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Bigby's Interposing Hand. No save, only SR. -10 attack rolls for them + // is good if they have highish BAB. Check here. + // (Count as stun for immunity - that is anyting that stops them moving (daze included)) + // - Should affect mind-immune people. Bioware will fix this, been told. No mind check + if(RangeLongValid && !AI_CompareTimeStopStored(SPELL_BIGBYS_INTERPOSING_HAND) && + !GetHasSpellEffect(SPELL_BIGBYS_INTERPOSING_HAND, GlobalSpellTarget) && + GetBaseAttackBonus(GlobalSpellTarget) >= GlobalOurHitDice/i2) + { + // Bigby's Interposing Hand. Level 5 (Mage) No save, only SR. -10 attack rolls for target + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_INTERPOSING_HAND, SpellHostRanged, i50, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } +// AOE: +// [Cloudkill] - Acid damage, and kills level 7s or below. AOE. +// [Cone of Cold] - Cone of damage, up to 15d6 to those in the cone. Reflex for none. +// [Firebrand] - Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage. +// [Circle of Doom] - 1d8 + 1/caster level in negative damage +// [Flame Strike] - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area. +// [Ball Lightning] - 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms) + + // Firebrand - good, because it hits only enemies (up to 15! thats plenty) + // and 1d6 damage each. Mainly, it doesn't hit allies :-D + if(SpellHostRanged && RangeMediumValid && !AI_CompareTimeStopStored(SPELL_FIREBRAND) && + (GetHasSpell(SPELL_FIREBRAND) || ItemHostRanged == SPELL_FIREBRAND)) + { + // 20M medium range, colossal area. Reflex save - doesn't hit allies too. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_COLOSSAL, i5, SAVING_THROW_REFLEX); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Firebrand Level 5 (Mage) Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage. + if(AI_ActionCastSpellRandom(SPELL_FIREBRAND, SpellHostRanged, i50, oAOE, i15, TRUE, ItemHostRanged)) return TRUE; + } + } + // Ball lightning - Medium spell, and missile storm. We cast this at the target. + if(RangeMediumValid && GlobalSeenSpell && + !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i5)) + { + // Ball Lightning. Level 5 (Mage) 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms) + if(AI_ActionCastSpellRandom(SPELL_BALL_LIGHTNING, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE; + } + + // (Shades) Cone of Cold. Shades = Never hits allies (Still same saves ETC). + if(SpellHostAreaInd && RangeShortValid && + (GetHasSpell(SPELL_SHADES_CONE_OF_COLD) || ItemHostAreaInd == SPELL_SHADES_CONE_OF_COLD || + GetHasSpell(SPELL_CONE_OF_COLD) || ItemHostAreaInd == SPELL_CONE_OF_COLD)) + { + // Small-distance, cone-based spell. Reflex save + oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, i5, SAVING_THROW_REFLEX, SHAPE_SPELLCONE); + // Is it valid? 100% chance of casting (Can't be bothered to create a special random version of SubSpell) + if(GetIsObjectValid(oAOE)) + { + // Cone of Cold. Level 5 (Mage). Cone of damage, up to 15d6 to those in the cone. Reflex for none. + // Shades vesion is great though - never affects allies. + if(AI_ActionCastSubSpell(SPELL_SHADES_CONE_OF_COLD, SpellHostAreaInd, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE; + + // Cone of Cold. Level 5 (Mage). Cone of damage, up to 15d6 to those in the cone. Reflex for none. + if(AI_ActionCastSpellRandom(SPELL_CONE_OF_COLD, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Flame Strike - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area. + if(SpellHostAreaInd && RangeMediumValid && + (GetHasSpell(SPELL_FLAME_STRIKE) || ItemHostAreaInd == SPELL_FLAME_STRIKE)) + { + // Small-distance, cone-based spell. Reflex save + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i5, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Flame Strike. Level 5 (Cleric) Up to 15d6 in Fire + Divine damage. Reflex based. Medium area. + if(AI_ActionCastSpellRandom(SPELL_FLAME_STRIKE, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Circle of Doom - 1d8 + 1/caster level in negative damage + if(SpellHostAreaDis && RangeMediumValid && + (GetHasSpell(SPELL_CIRCLE_OF_DOOM) || ItemHostAreaDis == SPELL_CIRCLE_OF_DOOM)) + { + // Shpere, medium and reflex save. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i5, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Circle of Doom. Level 5 (Cleric) 1d8 + 1/caster level in negative damage + if(AI_ActionCastSpellRandom(SPELL_CIRCLE_OF_DOOM, SpellHostAreaDis, i40, oAOE, i15, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Cloudkill. Acid damage, and kills level 7s or below. AOE. Quite good persistant damage. + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_CLOUDKILL) || ItemHostAreaInd == SPELL_CLOUDKILL)) + { + // No save (fortitude only halfs damage) but large (5M across) AOE - and long range + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i5, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Cloudkill. Level 5 (Mage) Acid damage, and kills level 7s or below. AOE. Quite good persistant damage. + if(AI_ActionCastSpellRandom(SPELL_CLOUDKILL, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE; + } + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Level 5 summons + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i5 && + (GlobalOurHitDice <= i14 || GlobalMeleeAttackers <= i2)) + { + // Summon Monster V (5). Level 5 (Most classes) Dire Bear + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_V, i15, i5)) return TRUE; + // Lesser Planar Binding. Level 5 (Mage). Summons Imp (Evil), Slaad Red (Neutral), Lantern Archon (Good) + if(AI_ActionCastSummonSpell(SPELL_LESSER_PLANAR_BINDING, i15, i5)) return TRUE; + } + + // Single spells again. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i5) + { + // Slay living. 60% chance to cast - it is a touch spell. + if(RangeTouchValid && !AI_CompareTimeStopStored(SPELL_SLAY_LIVING) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i5)) + { + // Slay Living. Level 5 (Cleric). Touch attack + Fortitude Save, creature dies, else d6(3) negative damage. + if(AI_ActionCastSpellRandom(SPELL_SLAY_LIVING, SpellHostRanged, i30, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Hold monster - Paralyze's an enemy monster (any race) + // Decent enough, if not stunned already - 60% + if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_HOLD_MONSTER) && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i5)) + { + // Hold monster - Paralyze's an enemy monster (any race) + if(AI_ActionCastSpellRandom(SPELL_HOLD_MONSTER, SpellHostRanged, i20, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Inferno - the Druids Acid Arrow (more damage, however, each round). + // ---- No save!! + if(RangeShortValid) + { + // Inferno. Level 5 (Druid) 2d6 Fire damage/round like acid arrow. No save, only SR. + if(AI_ActionCastSpellRandom(SPELL_INFERNO, SpellHostRanged, i20, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; + } + // Bigby's Interposing Hand. No save, only SR. -10 attack rolls for them + // is good if they have highish BAB. Check here. + // (Count as stun for immunity - that is anyting that stops them moving (daze included)) + // - Should affect mind-immune people. Bioware will fix this, been told. No mind check + if(RangeLongValid && !AI_CompareTimeStopStored(SPELL_BIGBYS_INTERPOSING_HAND) && + !GetHasSpellEffect(SPELL_BIGBYS_INTERPOSING_HAND, GlobalSpellTarget) && + GetBaseAttackBonus(GlobalSpellTarget) >= GlobalOurHitDice/i2) + { + // Bigby's Interposing Hand. Level 5 (Mage) No save, only SR. -10 attack rolls for target + if(AI_ActionCastSpellRandom(SPELL_BIGBYS_INTERPOSING_HAND, SpellHostRanged, i20, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; + } + } + + // Pass random spell not cast, but have, here. + if(AI_ActionCastBackupRandomSpell()) return TRUE; + +/*::4444444444444444444444444444444444444444444444444444444444444444444444444444 + Level 4 Spells. +//::4444444444444444444444444444444444444444444444444444444444444444444444444444 + Thoughts -Quite a few decent enough spells here. Nothing to catch your eye, + except maybe the overused Ice Storm or Wall of Fire. hammer of the gods + is a good clerical AOE spell and phantasmal killer is probably the first + Instant-death spells (but requires 2 saves1). + + AOE: + [Confusion] - confuse people Vs Mind Will in an area. + [Evard's Black Tentacles] - Rolls VS + [Fear] - Save VS mind will or fear, in an area. + [Ice Storm] - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save! +S [Isaac's Lesser Missile Storm] - 1d6 per each 1-10 missile divided around enemies in AOE. + [Wall of Fire] - 4d6 fire reflex damage in a retangle persistant AOE + [Hammer of the Gods] - divine damage d8(half caster level) to 5. Can Daze. Will save. + [War Cry] Will save or fear (like a howl) and all allies get +2 attack/damage + + Single: + [Bestow Curse] - Curse = -2 in all stats against a fortitude save. + [Charm Monster] - Charm a single monster (any race) VS will and mind + [Contagion] - Disease (Static DC!) if fail fortitude save + [Enervation] - 1d4 negative levels, fortitude save + [Phantasmal Killer] Will save (Illusion) then fort save, or death. +S [Inflict Critical Wounds] - 3d8 + 1/caster level to +20, in negative energy. + [Poison] - Inflicts poison (sadly, static fortitude save) + + X [Lesser Spell Breach] - Breach an amount of spell protections and lowers SR. + + Defender: + [Elemental Shield] +50% Cold/Fire resistance. 1d6 + Caster level reflected damage to melee attackers. + X [Improved Invisibility] +50% consealment, invisiblity (unseen) until hostile action. Cast above to at least conseal + [Minor Globe of Invulnerability] - 0/1/2/3 level hostile spells immune to. + [Stoneskin] - 10/+5 DR. We cast this here, sorceror behaviour and backup for greater stoneskin + [Death Ward] - Death Immunity - death magic. + + Summon: + [Summon Creature IV (4)] Summons a Dire Spider + + Other: + X [Polymorph Self] - Troll, umber Hulk, Pixie, Zombie, Giant spider. Cast after spells to fight. + X [Shadow Conjuration] - Shadow spells. + [Cure Critical Wounds] - Cures wounds + X [Divine Power] + Temp HP, +BAB, +Strength to 18. Done before melee + [Freedom of Movement] - Slowing removed. Immunity to slowing effects and stuff. + [Neutralize Poison] - Rids poison + [Restoration] - Restores lost statistics +S [Mass Camouflage] +10 Hide +H X [Holy Sword] - Paladins sword gains the "Holy Avenger" property - +1d6 divine, +5 enchant, 25% dispel. +//::44444444444444444444444444444444444444444444444444444444444444444444444444*/ + + // Jump out if we don't want to cast level 4 spells. + if(iLowestSpellLevel > i4) return FALSE; + + if(IsFirstRunThrough) + { + // Cast normal stoneskin to prepare for greater stoneskin going down. + // as par greater stoneskin, we must also have used level 5, 6, 7 8 and 9 spells + // anyway! That, and this is good for sorcerors. + if(!GetHasSpellEffect(SPELL_STONESKIN)) + { + // Stoneskin. Level 4 (Mage) 5 (Druid) 10/+5 phisical damage reduction + if(AI_ActionCastSpell(SPELL_STONESKIN, SpellProSinTar, OBJECT_SELF, i14, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + + // Check for Arcane Archer feats here + if(GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER)) + { + // Arrow of death. DC20 or die. + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_ARROW_OF_DEATH, GlobalSpellTarget)) return TRUE; + // Fireball arrow - won't harm allies + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_IMBUE_ARROW, GlobalSpellTarget)) return TRUE; + // Seeker Arrow is cool + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_SEEKER_ARROW_2, GlobalSpellTarget)) return TRUE; + // Hail of arrows is neat + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_HAIL_OF_ARROWS, GlobalSpellTarget)) return TRUE; + // Seeker Arrow is cool + if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_SEEKER_ARROW_1, GlobalSpellTarget)) return TRUE; + } + + // Elemental Shield is a good spell - reflected damage. It is normally + // cast above with more melee attackers. Here by backup. Shield-based spell + // (EffectDamageShield()) + + // Elemental Shield. Level 5 (Mage) +50% Cold/Fire resistance. 1d6 + Caster level reflected damage to melee attackers. + // (Use this but with imput i4) + if(AI_SpellWrapperShieldProtections(i4)) return TRUE; + + // Minor globe backup casting. + if(AI_SpellWrapperGlobeProtections(iLowestSpellLevel)) return TRUE; + + // Not bad, immunity to death/ + // - Might add in opposing casting if they start casting death spells. + if(!AI_GetAIHaveSpellsEffect(GlobalHasDeathWardSpell)) + { + // Death Ward. Level 4 (Cleric/Paladin) 5 (Druid). Immunity to death (Death-based spells nromally, like Wail). + if(AI_ActionCastSpell(SPELL_DEATH_WARD, SpellEnhSinTar, OBJECT_SELF, i14, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + } + + // Some lower end ally buffs + + // Spell Resistance. + if(AI_ActionCastAllyBuffSpell(f10, i60, SPELL_SPELL_RESISTANCE)) return TRUE; + + if(GetBaseAttackBonus(GlobalSpellTarget) < GlobalSpellTargetHitDice - i2) + { + // Death Ward if we can see an enemy spellcaster. + if(AI_ActionCastAllyBuffSpell(f10, i60, SPELL_DEATH_WARD)) return TRUE; + } + } + + // BAB check + // - BAB checks check our BASE attack bonus, no modifiers. Basically, as + // normally even mages have a +X to attack, this provides a good indicator + // if we are going to easy, or very easily, hit the enemy. + // - Clerics, Druids and Bards must be able to hit even better then normal. + if(IsFirstRunThrough && !SRA && + GlobalOurChosenClass != CLASS_TYPE_WIZARD && + GlobalOurChosenClass != CLASS_TYPE_SORCERER && + GlobalOurChosenClass != CLASS_TYPE_FEY) + { + // If a druid, cleric, and so on, have to be able to hit better then + // more then normal + if(GlobalOurChosenClass == CLASS_TYPE_CLERIC || + GlobalOurChosenClass == CLASS_TYPE_DRUID || + GlobalOurChosenClass == CLASS_TYPE_BARD) + { + // BAB check for level 4 spells. + if(GlobalOurBaseAttackBonus - i5 >= GlobalMeleeTargetAC) return FALSE; + } + // Demons, fighters, anything else really. + else + { + // BAB check for level 4 spells. + if(GlobalOurBaseAttackBonus >= GlobalMeleeTargetAC) return FALSE; + } + } + + // Hostile spells +// Single: +// [Enervation] - 1d4 negative levels, fortitude save +// [Phantasmal Killer] Will save (Illusion) then fort save, or death. + +// [Poison] - Inflicts poison (sadly, static fortitude save) +// [Bestow Curse] - Curse = -2 in all stats against a fortitude save. +// [Charm Monster] - Charm a single monster (any race) VS will and mind +// [Contagion] - Disease (Static DC!) if fail fortitude save + + // Is it best to target with single-target spells first? + // Inflict critical, as well as some others, are quite powerful for level 3. + // 60-70% if favourable. + // - We don't cast Bestow Curse, Charm Monster, Poison, or Contagion above + // AOE spells. We cast critical wounds only at the very end. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i4 && + // All level 4, all fort saves + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i4)) + { + // Phantismal Killer. Will and Fortitude save VS death. :-) + if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_PHANTASMAL_KILLER) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i4)) + { + // Phantasmal Killer. Level 4 (Mage). Will save (Illusion) then fort save, or death. + if(AI_ActionCastSpellRandom(SPELL_PHANTASMAL_KILLER, SpellHostRanged, i50, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; + } + // Enervation. 1d4 negative levels. Short range + if(RangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Enervation. Level 4 (Mage). 1d4 negative levels, fortitude save + if(AI_ActionCastSpellRandom(SPELL_ENERVATION, SpellHostRanged, i40, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; + } + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + +// AOE: +// [Confusion] - confuse people Vs Mind Will in an area. +// [Evard's Black Tentacles] - Rolls VS AC and damage 1-5 lots of 2d6 blud. +// [Fear] - Save VS mind will or fear, in an area. +// [Ice Storm] - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save! +// [Isaac's Lesser Missile Storm] - 1d6 per each 1-10 missile divided around enemies in AOE. +// [Wall of Fire] - 4d6 fire reflex damage in a retangle persistant AOE +// [Hammer of the Gods] - divine damage d8(half caster level) to 5. Can Daze. Will save. +// [War Cry] Will save or fear (like a howl) and all allies get +2 attack/damage + + if(GlobalNormalSpellsNoEffectLevel < i4) + { + if(RangeLongValid) + { + // Lesser missile storm. 1d6/missile. 1-10 missiles basically. Enemies only! + // Just cast at the enemy. + // Isaac's Lesser Missile Storm. Level 4 (Mage) 1d6 per each 1-10 missile divided around enemies in AOE. + if(AI_ActionCastSpellRandom(SPELL_ISAACS_LESSER_MISSILE_STORM, SpellHostRanged, i70, GlobalSpellTarget, i14, TRUE, ItemHostRanged)) return TRUE; + } + + // War cry here. No need to check if they run off - helps allies. + if(GlobalSpellTargetRange <= RADIUS_SIZE_COLOSSAL) + { + // War Cry. Level 4 (Bard) Will save or fear (like a howl) and all allies get +2 attack/damage + if(AI_ActionCastSpellRandom(SPELL_WAR_CRY, SpellHostAreaDis, i30, OBJECT_SELF, i14, TRUE, ItemHostAreaDis)) return TRUE; + } + } + + // Ice storm. plenty of damage :-) + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_ICE_STORM) || ItemHostAreaInd == SPELL_ICE_STORM)) + { + // Shpere, huge and no save :-) - long range too. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i4, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Ice Storm. Level 4 (Mage) 5 (Druid) 6 (Bard) - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save! + if(AI_ActionCastSpellRandom(SPELL_ICE_STORM, SpellHostAreaInd, i50, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Confusion - confusion! Decent especially against PC's. + if(SpellHostAreaDis && RangeMediumValid && + (GetHasSpell(SPELL_CONFUSION) || ItemHostAreaDis == SPELL_CONFUSION)) + { + // Shpere, huge and no save :-) + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireHostile); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Ice Storm. Level 4 (Mage) 5 (Druid) 6 (Bard) - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save! + if(AI_ActionCastSpellRandom(SPELL_CONFUSION, SpellHostAreaDis, i50, oAOE, i14, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Hammer of the Gods. Divine damage is good, as well as save VS daze. + if(SpellHostAreaDis && RangeMediumValid && + (GetHasSpell(SPELL_HAMMER_OF_THE_GODS) || ItemHostAreaDis == SPELL_HAMMER_OF_THE_GODS)) + { + // Shpere, no friends affected, we cast even if saves VS will. :-) + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i4); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Hammer of the Gods. Level 4 (Cleric). Divine damage d8(half caster level) to 5d8. Can Daze. Will save. + if(AI_ActionCastSpellRandom(SPELL_HAMMER_OF_THE_GODS, SpellHostAreaDis, i40, oAOE, i14, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Fear - fear! + if(SpellHostAreaDis && RangeMediumValid && + (GetHasSpell(SPELL_FEAR) || ItemHostAreaDis == SPELL_FEAR)) + { + // Shpere, no friends affected, we cast even if saves VS will. :-) + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireHostile); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Fear. Level 4 (Mage) 3 (Bard). Save VS mind will or fear, in an area. + if(AI_ActionCastSpellRandom(SPELL_FEAR, SpellHostAreaDis, i40, oAOE, i14, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Wall of Fire. + if(SpellHostAreaInd && RangeMediumValid && + (GetHasSpell(SPELL_WALL_OF_FIRE) || ItemHostAreaInd == SPELL_WALL_OF_FIRE) && + (GetHasSpell(SPELL_SHADES_WALL_OF_FIRE) || ItemHostAreaInd == SPELL_SHADES_WALL_OF_FIRE)) + { + // Ok, retangle. Take it as a medium sized sphere. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i4, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Shades version + if(AI_ActionCastSubSpell(SPELL_SHADES_WALL_OF_FIRE, SpellHostAreaInd, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE; + + // Wall of Fire. Level 4 (Mage) 5 (Druid) 4d6 fire reflex damage in a retangle persistant AOE + if(AI_ActionCastSpellRandom(SPELL_WALL_OF_FIRE, SpellHostAreaInd, i40, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Evard's Black Tentacles. AC attack and damage (fort or paralysis on enter) + // Casts if average HD is under 10 + if(SpellHostAreaInd && RangeMediumValid && GlobalAverageEnemyHD <= i10 && + (GetHasSpell(SPELL_EVARDS_BLACK_TENTACLES) || ItemHostAreaInd == SPELL_EVARDS_BLACK_TENTACLES)) + { + // 5M sized AOE spell, medium range. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i4, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 40% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Evard's Black Tentacles. Level 4 (Mage) AC attack and damage (2d6 x 1-5 hits) (fort or paralysis on enter) + if(AI_ActionCastSpellRandom(SPELL_EVARDS_BLACK_TENTACLES, SpellHostAreaInd, i30, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE; + } + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Level 4 summons + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i4 && + (GlobalOurHitDice <= i12 || GlobalMeleeAttackers <= i2)) + { + // Summon Monster IV (4). Level 4 (Most classes) Summons a Dire Spider + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_IV, i14, i4)) return TRUE; + } + +// [Enervation] - 1d4 negative levels, fortitude save +// [Phantasmal Killer] Will save (Illusion) then fort save, or death. +// [Inflict Critical Wounds] - 3d8 + 1/caster level to +20, in negative energy. + +// [Poison] - Inflicts poison (sadly, static fortitude save) +// [Bestow Curse] - Curse = -2 in all stats against a fortitude save. +// [Charm Monster] - Charm a single monster (any race) VS will and mind +// [Contagion] - Disease (Static DC!) if fail fortitude save + + // All single target spells, even poison and so on. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i4) + { + // All but charm are fortitude based. + if(!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i4)) + { + // Phantismal Killer. Will and Fortitude save VS death. :-) + if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_PHANTASMAL_KILLER) && + !AI_GetSpellTargetImmunity(GlobalImmunityDeath) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i4)) + { + // Phantasmal Killer. Level 4 (Mage). Will save (Illusion) then fort save, or death. + if(AI_ActionCastSpellRandom(SPELL_PHANTASMAL_KILLER, SpellHostRanged, i50, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; + } + // Enervation. 1d4 negative levels. Short range + if(RangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Enervation. Level 4 (Mage). 1d4 negative levels, fortitude save + if(AI_ActionCastSpellRandom(SPELL_ENERVATION, SpellHostRanged, i40, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; + } + // Contagion. Disease! Quite good, but still check spell save DC at fort above. + // Necromancy. + if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityDisease)) + { + // Blackguard Ability + if(AI_ActionUseFeatOnObject(FEAT_CONTAGION, GlobalMeleeTarget)) return TRUE; + + // Contagion. Level 4 (Mage) 3 (Cleric/Bard). Random disease (Set DC's!) against a fortitude saving throw. + if(AI_ActionCastSpellRandom(SPELL_CONTAGION, SpellHostTouch, i40, GlobalSpellTarget, i14, FALSE, ItemHostTouch)) return TRUE; + } + // Poison - Inflicts poison (sadly, static fortitude save) Necro spell. + if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityPoison)) + { + // Poison. Level 4 (Cleric) 3 (Druid) Large Scorion Venom poison (Set DC!) against a fortitude saving throw. + if(AI_ActionCastSpellRandom(SPELL_POISON, SpellHostRanged, i40, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; + } + // Bestow Curse - Inflicts poison (sadly, static fortitude save) Necro spell. + if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityCurse)) + { + // Bestow Curse. Level 4 (Mage) 3 (Cleric/Bard) -2 to all stats VS Fortitude save. + if(AI_ActionCastSpellRandom(SPELL_BESTOW_CURSE, SpellHostTouch, i40, GlobalSpellTarget, i14, FALSE, ItemHostTouch)) return TRUE; + } + } + // Charm Monster. Charms any monster. Will save, mind based. + if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) && RangeShortValid && + !AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i4)) + { + // Charm Monster. Level 4 (mage) 3 (Bard). Charm any monster, will save to resist. + if(AI_ActionCastSpellRandom(SPELL_CHARM_MONSTER, SpellHostRanged, i30, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; + } + } + + // Backup cast anything we didn't choose to before + if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Critical wounds - damage at a touch attack. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i4 && RangeTouchValid && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Blackguard ability + if(AI_ActionUseFeatOnObject(FEAT_INFLICT_CRITICAL_WOUNDS, GlobalSpellTarget)) return TRUE; + + // Inflict Critical Wounds. Level 4 (Cleric) 3d8 + 1/caster level to +20, in negative energy. + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_CRITICAL_WOUNDS, SpellHostTouch, GlobalSpellTarget)) return TRUE; + } + +/*::3333333333333333333333333333333333333333333333333333333333333333333333333333 + Level 3 Spells. +//::3333333333333333333333333333333333333333333333333333333333333333333333333333 + Thoughts - This is a level where mages pick up - IE fireball! Many decent AOE + and single-target spells, which scale up pretty well for mid and high level + casters. Also dispels level 2 protections before other things. + + AOE: + [Fireball] - THE ONE SPELL TO RULE THEM ALL! - ok, basically the 1 D&D spell + which is most famous - and frequently the last word a mage says before it kills him! + up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...) +S [Gust of Wind] Knockdown enemies VS reflex. Main thing is Dispeling AOE's, and 1 is always kept in reserve. + [Lightning Bolt] An "oh, other spell" to fireball. Does a different damage type. can be more useful - different shape! Can't hit caster! 10d6 reflex electrical + [Negative Energy Burst] 1d8 + 1-20 (caster level) negative damage, and -1 STR/4 caster levels. Heals undead. + [Slow] -50% speed, -1 Attack, -4 AC (I think) to those in an AOE - doesn't hit allies. + [Stinking Cloud] - Dazes those in the AOE, if fail VS will. + [Call Lightning] - Lightning damage - To 10d6 reflex electrical. Never hits allies. Smaller AOE then fireball +S [Spike Growth] - Damage to those in the AOE, and slow for 24 Hours! +H [Mestals Acid Breath] - A cone of up to 10d6 (acid) damage. Reflex save. +H [Scintillating Sphere] - Explosion of up to 10d6 Damage (Electical) - An electiric fireball (reflex save) +H [Glyph of Warding] - AOE, if entered, does up to 1d6/2 caster levels (to 5d6) damage. + + [Dispel Magic] - Dispels all magic (at a max of +10 check) or 1 from all in AOE + + Single: + [Flame Arrow] 4d6 Reflex Fire Damage for each missile - 1/4 caster levels. + [Hold Person] Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save. + [Vampiric Touch] 1d6(caster level/2) in negative damage, heals us with temp HP. + [Dominate Animal] Dominates an animal only. Will save. +S [Quillfire] 1 quill at 1d8 + 1-5 damage. Scorpion poison too. + [Searing Light] Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead +S [Inflict Serious Wounds] - Touch attack, hit means 3d8 + 1-15 damage. +H [Healing Sting] - 1d6 + 1/Caster evel damage, and healed for that amount. Fort save for none. +H [Infestation of Maggots] - 1d4 Temp constitution damage/round. (1 round/caster level) + + Defender: +S X [Displacement] - 50% consealment. Cast above to conseal ourselves sooner (50% missing is good! stoneskin lasts twice as long!) + [Magic Circle Against Alignment] +AC, +mind immunity etc. In a persistant AOE around caster. + X [Protection From Elements] 30/- elemental protection, until 40 damage. + [Negative Energy Protection] Immunity to negative energy +S [Wounding Whispers] 1d6 + Caster level in refelected sonic damage. +H [Magical Vestment] - Gives 1 suit of armor/shield a +1 AC bonus/3 caster levels (to +5) + + Summon: + [Animate Dead] - Skeleton or zomie summon. Tough DR, long lasting, but hard to heal. + [Summon Creature III] - Summons a Dire Wolf. + + Other: + X [Clairaudience/Clairvoyance] +20 spot. + [Clarity] - Mind resistance, clears hostile mind effects. +XXXX[Find Traps] - Finds and disarms traps. Never cast - unless I add it for PC traps. + X [Haste] +1 action. +4 AC. +150% speed. Good spell, cast right near top for maximum effect. + X [Invisibility Sphere] - Allies in area become invisible. Cast above (nearly first spell!) if want to go invis. +S [Greater Magic Fang] - Helps animal companion a lot :-) + X [Cure Serious Wounds] 3d8 + 1-15 damage healed. + X [Invisibility Purge] - AOE around us which removes Invisiblity effects. Cast as special + X [Prayer] +HP, +Attack +Damage for one person. Cast just before melee + X [Remove Blindness/Deafness] - Removes blindness and deafness! + X [Remove Curse] - " " curse + X [Remove Disease] - " " Disease +H X [Greater Magical Weapon] - Up to +5 enchantment for weapon (cast before melee) +H X [Keen Edge] - Keens a weapon. +H X [Blade Thirst] - +3 enchantment bonus to 1 slashing weapon. +H X [Darkfire] - +1d6 + 1/caster level (to +10) in fire damage applied to non-magic weapon. + + Dispels all level 2 protections here. + + Bolts cast here. +//::33333333333333333333333333333333333333333333333333333333333333333333333333*/ + + // Jump out if we don't want to cast level 3 spells. + if(iLowestSpellLevel > i3) return FALSE; + + // Dispel level 2 protections + if(RangeMediumValid && !GlobalInTimeStop)// All medium spells. + { + // Dispel number need to be 2 for breach + if(GlobalDispelTargetHighestBreach >= i2) + { + // Wrapers Greater and Lesser Breach. + if(AI_ActionCastBreach()) return TRUE; + } + // Dispel >= 2 + if(GlobalDispelTargetHighestDispel >= i2) + { + // Wrappers the dispel spells + if(AI_ActionCastDispel()) return TRUE; + } + } + + if(IsFirstRunThrough) + { + // Greater magic fang + oAOE = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION); + if(GetIsObjectValid(oAOE) && !GetHasSpellEffect(SPELL_GREATER_MAGIC_FANG, oAOE)) + { + // Greater MAgic fang. Level 3 (Ranger/Druid) +Attack, +DR to animal companion + if(AI_ActionCastSpell(SPELL_GREATER_MAGIC_FANG, SpellProSinTar, oAOE, i13, FALSE, ItemProSinTar)) return TRUE; + } + + // Divine Shield - a feat, but a damn good one. + // Up to +5 Dodge AC. + if(GetHasFeat(FEAT_TURN_UNDEAD)) + { + // Divine Shield + if(AI_ActionUseFeatOnObject(FEAT_DIVINE_SHIELD)) return TRUE; + } + + // Magical vestment - this adds up to +5 AC to armor or shield! + // - Affects only our equipped armor. + oAOE = GetItemInSlot(INVENTORY_SLOT_CHEST); + // Makes sure it is valid + if(GetIsObjectValid(oAOE) && !GetHasSpellEffect(SPELL_MAGIC_VESTMENT)) + { + // Cast it at the armor + // Magical Vestment. Level 3 (Cleric) Gives 1 suit of armor/shield a +1 AC bonus/3 caster levels (to +5) + if(AI_ActionCastSpell(SPELL_MAGIC_VESTMENT, SpellEnhSinTar, oAOE, i13, FALSE, ItemEnhSinTar)) return TRUE; + } + + // Regenerations + if(AI_ActionCastAllyBuffSpell(f10, i50, SPELL_REGENERATE, SPELL_MONSTROUS_REGENERATION)) return TRUE; + + // Bulls Strength, Cats Grace, Endurance + if(AI_ActionCastAllyBuffSpell(f10, i50, SPELL_ENDURANCE, SPELL_CATS_GRACE, SPELL_ENDURANCE, iM1, SPELL_GREATER_BULLS_STRENGTH, SPELL_GREATER_CATS_GRACE)) return TRUE; + } + + // BAB check + // - BAB checks check our BASE attack bonus, no modifiers. Basically, as + // normally even mages have a +X to attack, this provides a good indicator + // if we are going to easy, or very easily, hit the enemy. + // - Clerics, Druids and Bards must be able to hit even better then normal. + if(IsFirstRunThrough && !SRA && + GlobalOurChosenClass != CLASS_TYPE_WIZARD && + GlobalOurChosenClass != CLASS_TYPE_SORCERER && + GlobalOurChosenClass != CLASS_TYPE_FEY) + { + // If a druid, cleric, and so on, have to be able to hit better then + // more then normal + if(GlobalOurChosenClass == CLASS_TYPE_CLERIC || + GlobalOurChosenClass == CLASS_TYPE_DRUID || + GlobalOurChosenClass == CLASS_TYPE_BARD) + { + // BAB check for level 3 spells. + // Must be able to hit them 100% of the time. + if(GlobalOurBaseAttackBonus >= GlobalMeleeTargetAC) return FALSE; + } + // Demons, fighters, anything else really. + else + { + // BAB check for level 3 spells. + // 75% chance of hitting them outright. + if(GlobalOurBaseAttackBonus + i5 >= GlobalMeleeTargetAC) return FALSE; + } + } + + // Bolts 80% chance. Not too good, but oh well. + if(GlobalSeenSpell && RangeMediumValid && d10() <= i8) + { + // All ability Draining Bolts. All creature, so no limits. + if(GetAbilityScore(GlobalSpellTarget, ABILITY_DEXTERITY) >= i10) + if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_DEXTERITY, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + if(GetAbilityScore(GlobalSpellTarget, ABILITY_WISDOM) >= i12) + if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_WISDOM, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + if(GetAbilityScore(GlobalSpellTarget, ABILITY_CONSTITUTION) >= i12) + if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_CONSTITUTION, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + if(GetAbilityScore(GlobalSpellTarget, ABILITY_STRENGTH) >= i12) + if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_STRENGTH, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + if(GetAbilityScore(GlobalSpellTarget, ABILITY_CHARISMA) >= i14) + if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_CHARISMA, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + if(GetAbilityScore(GlobalSpellTarget, ABILITY_INTELLIGENCE) >= i14) + if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_INTELLIGENCE, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + // And the damaging bolts/status affecting bolts. + // I really can't be bothered to add in a lot of checks for immunities. + // Might do later. + for(iCnt = SPELLABILITY_BOLT_ACID; iCnt <= SPELLABILITY_BOLT_WEB; iCnt++) + { + if(AI_ActionCastSpellRandom(iCnt, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + } + // Manticore spikes + if(AI_ActionCastSpellRandom(SPELLABILITY_MANTICORE_SPIKES, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + // Shifter one + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_GWILDSHAPE_SPIKES, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + // Azer blast + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_AZER_FIRE_BLAST, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + // Shadow attack + if(!GetHasSpellEffect(AI_SPELLABILITY_SHADOW_ATTACK, GlobalSpellTarget)) + { + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_SHADOW_ATTACK, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + } + // Petrify last. + if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify)) + { + if(AI_ActionCastSpellRandom(SPELLABILITY_TOUCH_PETRIFY, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; + } + + // Backup casting + if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + +// Hostile spells - randomise a bit (remember, some are cast sooner still) + +// Single: +// [Flame Arrow] 4d6 Reflex Fire Damage for each missile - 1/4 caster levels. +// [Hold Person] Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save. +// [Searing Light] Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead +// [Dominate Animal] Dominates an animal only. Will save. +// [Quillfire] 1 quill at 1d8 + 1-5 damage. Scorpion poison too. +// [Vampiric Touch] 1d6(caster level/2) in negative damage, heals us with temp HP. +// [Healing Sting] - 1d6 + 1/Caster evel damage, and healed for that amount. For save for none. +// [Infestation of Maggots] - 1d4 Temp constitution damage/round. (1 round/caster level) + +// [Inflict Serious Wounds] - Touch attack, hit means 3d8 + 1-15 damage. + + // Is it best to target with single-target spells first? + // Flame arrow, searing light and hold person are quite effective. + // 60-70% if favourable. + // - We don't cast inflict serious wounds here. GetHasSpell bodges with spontaeous spells. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i3) + { + // Flame arrow - not THE best spell, but well worth it. Also it is long range. + if(RangeLongValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i3)) + { + // Flame Arrow. Level 3 (Mage) 4d6 Reflex Fire Damage for each missile - 1/4 caster levels. + if(AI_ActionCastSpellRandom(SPELL_FLAME_ARROW, SpellHostRanged, i60, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + // All others are in medium range or smaller + if(RangeMediumValid) + { + // Hold Person - must be playable race, of course. Still quite powerful - as it paralyzes. + if(!AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3)) + { + // Hold Person. Level 3 (Mage) 2 (Cleric/Bard) Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save. + if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i40, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + // Searing light does good type of damage - divine, and is alright damage amount (up to 5d8) and more importantly, no save! + // Searing Light. Level 3 (Cleric). Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead + if(AI_ActionCastSpellRandom(SPELL_SEARING_LIGHT, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + // Dominate Animal. Must be an animal, duh! Dominates an animal only. Will save. + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3)) + { + // Dominate Animal. Level 3 (Druid) Dominates an animal only. Will save. + if(AI_ActionCastSpellRandom(SPELL_DOMINATE_ANIMAL, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeShortValid) + { + // Quillfire. Poison isn't bad, and a little damage :-) + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3)) + { + // Quillfire. Level 3 (Mage) 1 quill at 1d8 + 1-5 damage. Scorpion poison too. + if(AI_ActionCastSpellRandom(SPELL_QUILLFIRE, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeTouchValid) + { + // These 3 are necromantic + if(!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy)) + { + // Infestation of maggots - lots of CON damage over time. :-) + if(!GetHasSpellEffect(SPELL_INFESTATION_OF_MAGGOTS, GlobalSpellTarget)) + { + // Infestation of Maggots. Level 3 (Druid) 1d4 Temp constitution damage/round. (1 round/caster level) + if(AI_ActionCastSpellRandom(SPELL_INFESTATION_OF_MAGGOTS, SpellHostTouch, i40, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; + } + // These 2 are negative energy + if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Healing sting isn't too bad. At least no immunities + // and levels up OK. Negative energy damage, however. + if(!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i3)) + { + // Healing Sting. Level 3 (Druid). 1d6 + 1/Caster evel damage, and healed for that amount. Fort save for none. + if(AI_ActionCastSpellRandom(SPELL_HEALING_STING, SpellHostTouch, i40, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; + } + // Vampiric touch - good spell and scales nicely, and + // heals :-D + // Vampiric Touch. Level 3 (Mage) 1d6(caster level/2) in negative damage, heals us with temp HP. + if(AI_ActionCastSpellRandom(SPELL_VAMPIRIC_TOUCH, SpellHostTouch, i30, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; + } + } + } + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + // AOE spells +// [Fireball] - Up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...) +// [Call Lightning] - Lightning damage - To 10d6 reflex electrical. Never hits allies. Smaller AOE then fireball +// [Lightning Bolt] An "oh, other spell" to fireball. Does a different damage type. can be more useful - different shape! Can't hit caster! 10d6 reflex electrical +// [Slow] -50% speed, -1 Attack, -4 AC (I think) to those in an AOE - doesn't hit allies. +// [Negative Energy Burst] 1d8 + 1-20 (caster level) negative damage, and -1 STR/4 caster levels. Heals undead. +// [Stinking Cloud] - Dazes those in the AOE, if fail VS will. +// [Spike Growth] - Damage to those in the AOE, and slow for 24 Hours! +// [Gust of Wind] Knockdown enemies VS reflex. Main thing is Dispeling AOE's, and 1 is always kept in reserve. +// [Mestals Acid Breath] - A cone of up to 10d6 (acid) damage. Reflex save. +// [Scintillating Sphere] - Explosion of up to 10d6 Damage (Electical) - An electiric fireball (reflex save) +// [Glyph of Warding] - AOE, if entered, does up to 1d6/2 caster levels (to 5d6) damage. + + // Fireball is fun fun fun! :-D Shadow version first - doesn't hit allies. + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_SHADES_FIREBALL) || ItemHostAreaInd == SPELL_SHADES_FIREBALL)) + { + // Huge, long ranged, reflex based save. Shades == No enemies hit. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX); + // Is it valid? 100% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // (Shades) Fireball. Level 3 (Mage) Up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...) + if(AI_ActionCastSubSpell(SPELL_SHADES_FIREBALL, SpellHostAreaInd, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Real fireball is hot! (Ban pun there...) + // Scintillating Sphere is also fire-ball like, but electrical damage *shrugs* + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_FIREBALL) || ItemHostAreaInd == SPELL_FIREBALL || + GetHasSpell(SPELL_SCINTILLATING_SPHERE) || ItemHostAreaInd == SPELL_SCINTILLATING_SPHERE)) + { + // Huge, long ranged, reflex based save. Normal = reaction friendly. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 70% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Fireball. Level 3 (Mage) Up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...) + if(AI_ActionCastSpellRandom(SPELL_FIREBALL, SpellHostAreaInd, i60, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE; + + // Scintillating Sphere. Level 3 (Mage) Explosion of up to 10d6 Damage (Electical) - An electiric fireball (reflex save) + if(AI_ActionCastSpellRandom(SPELL_SCINTILLATING_SPHERE, SpellHostAreaInd, i60, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Call Lightning - Never hits allies, so excelent to cast :-) + if(SpellHostAreaDis && RangeLongValid && + (GetHasSpell(SPELL_CALL_LIGHTNING) || ItemHostAreaDis == SPELL_CALL_LIGHTNING)) + { + // Huge, long ranged, reflex based save. No enemies hit! + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX); + // Is it valid? 70% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Call Lightning. Level 3 (Druid) Lightning damage - To 10d6 reflex electrical. Never hits allies. Smaller AOE then fireball + if(AI_ActionCastSpellRandom(SPELL_CALL_LIGHTNING, SpellHostAreaDis, i60, oAOE, i13, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Mestils acid breath is on the standard 10d6 max damage. Acid + cone + if(SpellHostAreaDis && RangeShortValid && + (GetHasSpell(SPELL_MESTILS_ACID_BREATH) || ItemHostAreaDis == SPELL_MESTILS_ACID_BREATH)) + { + // Short, cone based. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, i3, SAVING_THROW_REFLEX, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Mestals Acid Breath. Level 3 (Mage) A cone of up to 10d6 (acid) damage. Reflex save. + if(AI_ActionCastSpellRandom(SPELL_MESTILS_ACID_BREATH, SpellHostAreaInd, i50, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Lightning Bolt. Basically, a fireball in a line. :-) + // Requires a target object to hit. + if(SpellHostAreaInd && RangeMediumValid && + (GetHasSpell(SPELL_LIGHTNING_BOLT) || ItemHostAreaInd == SPELL_LIGHTNING_BOLT)) + { + // Requries an object. Hits allies. 30 spell cylinder range. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, f30, i3, SAVING_THROW_REFLEX, SHAPE_SPELLCYLINDER, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting - only if seen + if(GetIsObjectValid(oAOE) && GetObjectSeen(oAOE)) + { + // Lightning Bolt. Level 3 (Mage) An "oh, other spell" to fireball. Does a different damage type. can be more useful - different shape! Can't hit caster! 10d6 reflex electrical + if(AI_ActionCastSpellRandom(SPELL_LIGHTNING_BOLT, SpellHostAreaInd, i40, oAOE, i13, FALSE, ItemHostAreaInd)) return TRUE; + } + } + // Slow is a good AOE - hits enemies, and slows them (Will save) + if(SpellHostAreaDis && RangeShortValid && + (GetHasSpell(SPELL_SLOW) || ItemHostAreaDis == SPELL_SLOW)) + { + // Slow - doesn't hit allies. Colossal range. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_COLOSSAL, i3, SAVING_THROW_WILL); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Slow. Level 3 (Mage/bard) -50% speed, -1 Attack, -4 AC (I think) to those in an AOE - doesn't hit allies. + if(AI_ActionCastSpellRandom(SPELL_SLOW, SpellHostAreaDis, i40, oAOE, i13, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Negative Energy Burst. Negative damage (to non-undead) and strength loss. + if(SpellHostAreaDis && RangeMediumValid && + (GetHasSpell(SPELL_NEGATIVE_ENERGY_BURST) || ItemHostAreaDis == SPELL_NEGATIVE_ENERGY_BURST)) + { + // Necromantic spell, hits allies. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i3, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly, FALSE, TRUE); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Negative Energy Burst. Level 3 (Mage) 1d8 + 1-20 (caster level) negative damage, and -1 STR/4 caster levels. Heals undead. + if(AI_ActionCastSpellRandom(SPELL_NEGATIVE_ENERGY_BURST, SpellHostAreaDis, i40, oAOE, i13, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Stinking Cloud. Daze, lots of daze (will based) + if(SpellHostAreaInd && RangeMediumValid && + (GetHasSpell(SPELL_STINKING_CLOUD) || ItemHostAreaInd == SPELL_STINKING_CLOUD)) + { + // Allies are hit, will save to be immune. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i3, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Stinking Cloud. Level 3 (Mage) Dazes those in the AOE, if fail VS will. + if(AI_ActionCastSpellRandom(SPELL_STINKING_CLOUD, SpellHostAreaInd, i40, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Spike Growth. Alright AOE - 30% speed decrease for 24HRS is the best bit. + if(SpellHostAreaInd && RangeMediumValid && + (GetHasSpell(SPELL_SPIKE_GROWTH) || ItemHostAreaInd == SPELL_SPIKE_GROWTH)) + { + // Allies are hit, will save to be immune. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i3, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Spike Growth. Level 3 (Druid) AOE - 30% speed decrease for 24HRS + 1d4 piercing damage/round. + if(AI_ActionCastSpellRandom(SPELL_SPIKE_GROWTH, SpellHostAreaInd, i40, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Gust of Wind. Always need 2 of these (1 to dispel AOE's) + if(SpellHostAreaInd && RangeMediumValid && + GetHasSpell(SPELL_GUST_OF_WIND) >= i2) + { + // Allies are hit, will save to be immune. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 50% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Gust of Wind. Level 3 (Bard/Mage) Dispels all AOE's in radius, and save VS fort for 3 round knockdown. + if(AI_ActionCastSpellRandom(SPELL_GUST_OF_WIND, SpellHostAreaInd, i40, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Glyph of Warding is cast last, lowest %, and just at spell target. + // This is because it is good for setting up traps, but not that good + // in combat, apart from the damage type. 30% + if(GlobalSeenSpell && RangeShortValid) + { + // Glyph of Warding. Level 3 (Mage) AOE, if entered, does up to 1d6/2 caster levels (to 5d6) damage. + if(AI_ActionCastSpellRandom(SPELL_GLYPH_OF_WARDING, SpellHostAreaInd, i20, GlobalSpellTarget, i13, TRUE, ItemHostAreaInd)) return TRUE; + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Level 3 summons + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i3 && + (GlobalOurHitDice <= i10 || GlobalMeleeAttackers <= i2)) + { + // Pale master + if(AI_ActionCastSummonSpell(AI_FEAT_PM_ANIMATE_DEAD, iM1, i3)) return TRUE; + + // Animate Dead. Level 3 (Mage). Skeleton or zomie summon. Tough DR, long lasting, but hard to heal. + if(AI_ActionCastSummonSpell(SPELL_ANIMATE_DEAD, i13, i3)) return TRUE; + + // Summon Monster III (3). Level 3 (Most classes) Summons a Dire Wolf. + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_III, i13, i3)) return TRUE; + } + + // Single target spells. + // - We don't cast inflict serious wounds. GetHasSpell bodges with spontaeous spells. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i3) + { + // Flame arrow - not THE best spell, but well worth it. Also it is long range. + if(RangeLongValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i3)) + { + // Flame Arrow. Level 3 (Mage) 4d6 Reflex Fire Damage for each missile - 1/4 caster levels. + if(AI_ActionCastSpellRandom(SPELL_FLAME_ARROW, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + // All others are in medium range or smaller + if(RangeMediumValid) + { + // Hold Person - must be playable race, of course. Still quite powerful - as it paralyzes. + if(!AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3)) + { + // Hold Person. Level 3 (Mage) 2 (Cleric/Bard) Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save. + if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i20, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + // Searing light does good type of damage - divine, and is alright damage amount (up to 5d8) and more importantly, no save! + // Searing Light. Level 3 (Cleric). Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead + if(AI_ActionCastSpellRandom(SPELL_SEARING_LIGHT, SpellHostRanged, i10, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + // Dominate Animal. Must be an animal, duh! Dominates an animal only. Will save. + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3)) + { + // Dominate Animal. Level 3 (Druid) Dominates an animal only. Will save. + if(AI_ActionCastSpellRandom(SPELL_DOMINATE_ANIMAL, SpellHostRanged, i10, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeShortValid) + { + // Quillfire. Poison isn't bad, and a little damage :-) + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3)) + { + // Quillfire. Level 3 (Mage) 1 quill at 1d8 + 1-5 damage. Scorpion poison too. + if(AI_ActionCastSpellRandom(SPELL_QUILLFIRE, SpellHostRanged, i10, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeTouchValid) + { + // These 3 are necromantic + if(!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy)) + { + // Infestation of maggots - lots of CON damage over time. :-) + if(!GetHasSpellEffect(SPELL_INFESTATION_OF_MAGGOTS, GlobalSpellTarget)) + { + // Infestation of Maggots. Level 3 (Druid) 1d4 Temp constitution damage/round. (1 round/caster level) + if(AI_ActionCastSpellRandom(SPELL_INFESTATION_OF_MAGGOTS, SpellHostTouch, i20, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; + } + // These 2 are negative energy + if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Healing sting isn't too bad. At least no immunities + // and levels up OK. Negative energy damage, however. + if(!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i3)) + { + // Healing Sting. Level 3 (Druid). 1d6 + 1/Caster evel damage, and healed for that amount. Fort save for none. + if(AI_ActionCastSpellRandom(SPELL_HEALING_STING, SpellHostTouch, i20, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; + } + // Vampiric touch - good spell and scales nicely, and + // heals :-D + // Vampiric Touch. Level 3 (Mage) 1d6(caster level/2) in negative damage, heals us with temp HP. + if(AI_ActionCastSpellRandom(SPELL_VAMPIRIC_TOUCH, SpellHostTouch, i10, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; + } + } + } + } + + // Backup spell + if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Serious wounds - damage at a touch attack. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i3 && RangeTouchValid && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Blackguard ability + if(AI_ActionUseFeatOnObject(FEAT_INFLICT_SERIOUS_WOUNDS, GlobalMeleeTarget)) return TRUE; + + // Inflict Serious Wounds. Level 3 (Cleric) Touch attack, hit means 3d8 + 1-15 negative damage. + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_SERIOUS_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE; + } + +/*::2222222222222222222222222222222222222222222222222222222222222222222222222222 + Level 2 Spells. +//::2222222222222222222222222222222222222222222222222222222222222222222222222222 + Thoughts - Lacking in AOE spells, as it is lower levels, it really + lacks a lot of spells anyway. A few decent damage spells, but many of the + spells are special-case healing, or cast elsewhere. Dispels all level 1 + protections here. + + AOE: +S [Balagarn's Iron Horn] Need to beat (Enemy STR + d20) with 20 + d20. If so, 6 second knockdown + [Web] - Reflex saves or entangles and stuck in the web. AOE, quite large, persistant. + [Sound Burst] - 1d8 sonic damage + Stun unless save VS will. + [Silence] - Silence AOE that moves with target. Applie silence to those in the AOE. +H [Cloud of Bewilderment] - Enemies only are stunned and blinded in the AOE. Fort save. +H [Gedlee's Electric Loop] - All in AOE have 1d6 Electric dam (to 5d6) + will save for 1 round stun. + + [Lesser Dispel] - Can be cast in silence (Stomatic only). Dispel up to +5 check. + + Single: + [Blindness/Deafness] - Blindness/deafness on Fort save failure. + [Ghoul Touch] - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby. + [Melf's Acid Arrow] - 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that +S [Tasha's Hideous Laughter] Knockdown for 1d3 rounds, if + [Charm Person or Animal] Charms an animal, or humanoid, if fail mind will save. +S [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save. + [Hold Animal] Paralyze's an amimal race target, if they fail a will save. +S [Inflict Moderate Wounds] 2d8 + 1-10 damage on a touch attack +H [Continual Flame] 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage. + + Defender: + X [Darkness]- Darkness. Supposedly caster can see. Basically an area of blindness countered with ultravision + [Ghostly Visage] - 5/+1 DR. 0/1 spell immunity. 10% consealment. + X [Invisibility] - Invisbile (not inaudible) until a hostile action. + X [Resist Elements] - Resists 20/- elemental damage, until 30 damage is done. Cast above + [Barkskin] - 1-6 = +3, 7-12 = +4. 13+ = +5 natural armor AC bonus. +H [Death Armor] - 1d4 + 1/2 caster levels (to +5) damage shield (Magical damage!) +H [Stone Bones] - An undead target gets +3 natural AC. + + Summon: + [Summon Creature II] - Summons a Dire Boar + + Other: + X [Bull's Strength] +Strength stat. Cast before melee normally. + X [Cat's Grace] *bulls strength* + X [Continual Flame] - Light for ever. + X [Eagle's Splendor] *bulls strength* + X [Endurance] *bulls strength* + X [Fox's Cunning] *bulls strength* + X [Knock] - Unlocks doors. Done OnBlocked. + X [Owl's Wisdom] *bulls strength* + X [See Invisibility] Cast in special cases. Pierces Invisiblity. + X [Ultravision] Lets you see fully in Darkness AOE's. +S X [Blood Frenzy] Rage - +3STR, CON - 2 AC. +S X [One with the Land] +4 Hide, Move Silently, Set Traps and Animal Empthy. not cast in this AI + X [Aid] +1d8 HP bonus, +1 attack/damage. + X [Cure Moderate Wounds] Cures 2d8 Damage + 1-10. + X [Lesser Restoration] Removes some bad effects. + X [Remove Paralysis] Removes paralysis! +H X [Flame Weapon] - 1d4 + 1/caster level (to +10) fire damage to a weapon. +H X [Aura of Glory] - +4 Char, and allies get +4 VS Fear effects. + +//::22222222222222222222222222222222222222222222222222222222222222222222222222*/ + + // Jump out if we don't want to cast level 2 spells. + if(iLowestSpellLevel > i2) return FALSE; + + if(IsFirstRunThrough) + { + // Barkskin (if not already) + if(!AI_GetAIHaveSpellsEffect(GlobalHasNaturalACSpell)) + { + if(GlobalOurRace == RACIAL_TYPE_UNDEAD) + { + // Stone bones - cast if we do not have a natural armor AC spell. + if(AI_ActionCastSpell(SPELL_STONE_BONES, SpellProSinTar, OBJECT_SELF, i12, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + // Barkskin. Level 2 (Druid). 1-6 = +3, 7-12 = +4. 13+ = +5 natural armor AC bonus. + if(AI_ActionCastSpell(SPELL_BARKSKIN, SpellProSinTar, OBJECT_SELF, i12, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + // See AC notes in level 1 spells. + + // Cast ally buff spells if we are a buffer + if(GlobalWeAreBuffer) + { + // Some AC protections + if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_BARKSKIN, SPELL_MAGE_ARMOR)) return TRUE; + } + + // All visage spells here. + if(AI_SpellWrapperVisageProtections(iLowestSpellLevel)) return TRUE; + + // Eyeball rays. Low stuff, but hey, whatever, eh? + // Random cast these. 3 random ones. + if(RangeMediumValid) + { + // Random cast. Each one has 30-50 % chance. Doesn't matter which we use! + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_EYEBALL_RAY_0, SpellHostRanged, i40, GlobalSpellTarget)) return TRUE; + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_EYEBALL_RAY_1, SpellHostRanged, i40, GlobalSpellTarget)) return TRUE; + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_EYEBALL_RAY_2, SpellHostRanged, i40, GlobalSpellTarget)) return TRUE; + + // Backup cast + if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + } + + // Dispel level 1 protections + if(RangeMediumValid && !GlobalInTimeStop)// All medium spells. + { + // Dispel number need to be 1 for breach + if(GlobalDispelTargetHighestBreach >= i1) + { + // Wrapers Greater and Lesser Breach. + if(AI_ActionCastBreach()) return TRUE; + } + // Dispel >= 1 + if(GlobalDispelTargetHighestDispel >= i1) + { + // Wrappers the dispel spells + if(AI_ActionCastDispel()) return TRUE; + } + } + + // BAB check + // - BAB checks check our BASE attack bonus, no modifiers. Basically, as + // normally even mages have a +X to attack, this provides a good indicator + // if we are going to easy, or very easily, hit the enemy. + // - Clerics, Druids and Bards must be able to hit even better then normal. + if(IsFirstRunThrough && !SRA && + GlobalOurChosenClass != CLASS_TYPE_WIZARD && + GlobalOurChosenClass != CLASS_TYPE_SORCERER && + GlobalOurChosenClass != CLASS_TYPE_FEY) + { + // If a druid, cleric, and so on, have to be able to hit better then + // more then normal + if(GlobalOurChosenClass == CLASS_TYPE_CLERIC || + GlobalOurChosenClass == CLASS_TYPE_DRUID || + GlobalOurChosenClass == CLASS_TYPE_BARD) + { + // BAB check for level 3 spells. + // Must be able to hit them 75% of the time. + if(GlobalOurBaseAttackBonus + i5 >= GlobalMeleeTargetAC) return FALSE; + } + // Demons, fighters, anything else really. + else + { + // BAB check for level 3 spells. + // 50% chance of hitting them outright. + if(GlobalOurBaseAttackBonus + i10 >= GlobalMeleeTargetAC) return FALSE; + } + } + + // Level 2 random hostile spell +// Single: +// [Melf's Acid Arrow] - 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that +// [Hold Animal] Paralyze's an amimal race target, if they fail a will save. +// [Blindness/Deafness] - Blindness/deafness on Fort save failure. +// [Tasha's Hideous Laughter] Knockdown for 1d3 rounds, if fail will save. -4 DC if different races +// [Charm Person or Animal] Charms an animal, or humanoid, if fail mind will save. +// [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save. +// [Ghoul Touch] - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby. +// [Continual Flame] 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage. + + // Is it best to target with single-target spells first? + // Acid arrow, flame lash all scale well, while blindness/deafness is a good spell too. + // 60-70% if favourable. + // - We don't cast inflict serious wounds here. GetHasSpell bodges with spontaeous spells. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i2) + { + // Ghoul Touch. Paralysis is good :-D + if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2)) + { + // Ghoul Touch. Level 2 (Mage) - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby. + if(AI_ActionCastSpellRandom(SPELL_GHOUL_TOUCH, SpellHostTouch, i50, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE; + } + if(RangeLongValid) + { + // Greater shadow conjuration + if(d10() <= i6) + { + if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_ACID_ARROW, SpellHostRanged, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Acid arrow - decent enough, and persistant damage, and no save. Long range too! + // Melf's Acid Arrow. Level 2 (Mage) 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that + if(AI_ActionCastSpellRandom(SPELL_MELFS_ACID_ARROW, SpellHostRanged, i60, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Continual flame isn't a bad spell - touch spell, and does + // continual damage (reflex based) + if(RangeTouchValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2)) + { + // Continual Flame. Level 2 (Mage) 3 (Cleric) 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage. + if(AI_ActionCastSpellRandom(SPELL_CONTINUAL_FLAME, SpellHostTouch, i60, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE; + } + // Flame Lash is an alright amount of flame damage. + if(RangeShortValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2)) + { + // [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save. + if(AI_ActionCastSpellRandom(SPELL_FLAME_LASH, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + if(RangeMediumValid) + { + // Hold Animal - animals are paralyzed on a will save failure. + if(GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Hold Animal. Level 2 (Druid/Ranger) Paralyze's an amimal race target, if they fail a will save. + if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Blindness/Deafness is a pretty good spell. + if(!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2)) + { + // Blindness/Deafness. Level 2 (Mage/Bard) 3 (Cleric) Blindness/deafness on Fort save failure. + if(AI_ActionCastSpellRandom(SPELL_BLINDNESS_AND_DEAFNESS, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Tasha's Hideous Laughter is knockdown - not too bad. + // - We don't bother with the +4/-4. + if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Tasha's Hideous Laughter. Level 2 (Bard/Mage) Knockdown for 1d3 rounds, if fail will save. -4 DC if different races + if(AI_ActionCastSpellRandom(SPELL_TASHAS_HIDEOUS_LAUGHTER, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeShortValid) + { + // Charm Person or Animal. Charms an animal, or humanoid, if fail mind will save. + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + (GetIsPlayableRacialType(GlobalSpellTarget) || GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Charm Person or Animal. Level 2 (Druid) Charms an animal, or humanoid, if fail mind will save. + if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON_OR_ANIMAL, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + } + + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + +// AOE: +// [Silence] - Silence AOE that moves with target. Applies silence to those in the AOE. +// [Web] - Reflex saves or entangles and stuck in the web. AOE, quite large, persistant. +// [Balagarn's Iron Horn] Need to beat (Enemy STR + d20) with 20 + d20. If so, 6 second knockdown +// [Sound Burst] - 1d8 sonic damage + Stun unless save VS will. +// [Cloud of Bewilderment] - Creatures are stunned and blinded in the AOE. Fort save. +// [Gedlee's Electric Loop] - All in AOE have 1d6 Electric dam (to 5d6) + will save for 1 round stun. + + // Silence - must be a mage or sorceror. No talent for this, BTW. Also + // note: can't have the spell's effects already, and must be over 10M away. + if(RangeLongValid && GlobalSpellTargetRange >= f10 && + !GetHasSpellEffect(SPELL_SILENCE, GlobalSpellTarget) && + (GetLevelByClass(CLASS_TYPE_WIZARD, GlobalSpellTarget) || + GetLevelByClass(CLASS_TYPE_SORCERER, GlobalSpellTarget) || + GetLevelByClass(CLASS_TYPE_CLERIC, GlobalSpellTarget))) + { + // Silence. Level 2 (Mage/Cleric/Bard) Silence AOE that moves with target. Applies silence to those in the AOE. + if(AI_ActionCastSpellRandom(SPELL_SILENCE, SpellOtherSpell, i60, GlobalSpellTarget, i12)) return TRUE; + } + // Gedlee's Electric Loop is a good damaging AOE spell - and the only mage level 2 one. + // - Note, it is set as Discriminate, but is really Indiscriminate (ReactionType) + if(SpellHostAreaDis && RangeShortValid && + (GetHasSpell(SPELL_GEDLEES_ELECTRIC_LOOP) || ItemHostAreaDis == SPELL_GEDLEES_ELECTRIC_LOOP)) + { + // Small, short ranged, and reflex save based. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_SMALL, i2, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 70% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Gedlee's Electric Loop. Level 2 (Mage) All in AOE have 1d6 Electric dam (to 5d6) + will save for 1 round stun. + if(AI_ActionCastSpellRandom(SPELL_GEDLEES_ELECTRIC_LOOP, SpellHostAreaDis, i60, oAOE, i12, TRUE, ItemHostAreaDis)) return TRUE; + } + } + // Web - sticky stuff. Illusion version first :-D + if(SpellHostAreaInd && RangeMediumValid && + (GetHasSpell(SPELL_WEB) || ItemHostAreaInd == SPELL_WEB || + GetHasSpell(SPELL_GREATER_SHADOW_CONJURATION_WEB) || ItemHostAreaInd == SPELL_GREATER_SHADOW_CONJURATION_WEB)) + { + // large AOE, medium ranged, reflex based save. Reaction friendly. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i2, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 70% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Shades vesion - shouldn't affect allies. + if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_WEB, SpellHostAreaInd, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE; + + // Beblith version + if(AI_ActionCastSpellRandom(AI_SPELLABILITY_BEBELITH_WEB, SpellHostAreaInd, i60, oAOE)) return TRUE; + + // Web. Level 2 (Mage) Entangles on reflex sve. + if(AI_ActionCastSpellRandom(SPELL_WEB, SpellHostAreaInd, i60, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Cloud of Bewilderment is an alright AOE - stun and blindness :-) + // Note - Only casts if the target is not immune to stun nor blindness! + if(SpellHostAreaInd && RangeMediumValid && + !AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + (GetHasSpell(SPELL_CLOUD_OF_BEWILDERMENT) || ItemHostAreaInd == SPELL_CLOUD_OF_BEWILDERMENT)) + { + // 5M radius (large) AOE, short ranged, fort based save. Reaction friendly. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_LARGE, i2, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Cloud of Bewilderment. Level 2 (Mage/Bard) Creatures are stunned and blinded in the AOE. Fort save. + // Web. Level 2 (Mage) Entangles on reflex sve. + if(AI_ActionCastSpellRandom(SPELL_CLOUD_OF_BEWILDERMENT, SpellHostAreaInd, i50, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Balagarn's Iron Horn is only a 6 second knockdown, not too bad. + // Mainly, it has no save. Caster gets +20 on d20, enemy gets strength bonus. + // It is a personal, affects us spell :-) + if(GlobalSpellTargetRange <= f5) + { + // Balagarn's Iron Horn. Level 2 (Mage) Need to beat (Enemy STR + d20) with 20 + d20. If so, 6 second knockdown + if(AI_ActionCastSpellRandom(SPELL_BALAGARNSIRONHORN, SpellHostAreaDis, i60, OBJECT_SELF, i12, TRUE, ItemHostAreaDis)) return TRUE; + } + // Sound burst - best part is the stun! Trust me! Long range too! + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_SOUND_BURST) || ItemHostAreaInd == SPELL_SOUND_BURST)) + { + // Medium AOE, medium ranged, will based save. Reaction friendly. + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i2, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Sound burst. Level 2 (cleric). 1d8 Sonic damage, and if fail will save, stunned for 2 rounds. + if(AI_ActionCastSpellRandom(SPELL_SOUND_BURST, SpellHostAreaInd, i50, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE; + } + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Level 2 summons + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i2 && + (GlobalOurHitDice <= i8 || GlobalMeleeAttackers <= i2)) + { + // Summon Monster II (2). Level 2 (Most classes) Summons a Dire Boar. + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_II, i12, i2)) return TRUE; + } + + // Single spells, lower %'s + // - We don't cast inflict serious wounds. GetHasSpell bodges with spontaeous spells. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i2) + { + // Ghoul Touch. Paralysis is good :-D + if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2)) + { + // Ghoul Touch. Level 2 (Mage) - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby. + if(AI_ActionCastSpellRandom(SPELL_GHOUL_TOUCH, SpellHostTouch, i30, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE; + } + if(RangeLongValid) + { + if(d10() <= i4) + { + // Greater shadow conjuration + if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_ACID_ARROW, SpellHostRanged, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Acid arrow - decent enough, and persistant damage, and no save. Long range too! + // Melf's Acid Arrow. Level 2 (Mage) 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that + if(AI_ActionCastSpellRandom(SPELL_MELFS_ACID_ARROW, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Continual flame isn't a bad spell - touch spell, and does + // continual damage (reflex based) + if(RangeTouchValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2)) + { + // Continual Flame. Level 2 (Mage) 3 (Cleric) 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage. + if(AI_ActionCastSpellRandom(SPELL_CONTINUAL_FLAME, SpellHostTouch, i30, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE; + } + // Flame Lash is an alright amount of flame damage. + if(RangeShortValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2)) + { + // [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save. + if(AI_ActionCastSpellRandom(SPELL_FLAME_LASH, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + if(RangeMediumValid) + { + // Hold Animal - animals are paralyzed on a will save failure. + if(GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL && + !AI_GetSpellTargetImmunity(GlobalImmunityStun) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Hold Animal. Level 2 (Druid/Ranger) Paralyze's an amimal race target, if they fail a will save. + if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Blindness/Deafness is a pretty good spell. + if(!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2)) + { + // Blindness/Deafness. Level 2 (Mage/Bard) 3 (Cleric) Blindness/deafness on Fort save failure. + if(AI_ActionCastSpellRandom(SPELL_BLINDNESS_AND_DEAFNESS, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + // Tasha's Hideous Laughter is knockdown - not too bad. + // - We don't bother with the +4/-4. + if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Tasha's Hideous Laughter. Level 2 (Bard/Mage) Knockdown for 1d3 rounds, if fail will save. -4 DC if different races + if(AI_ActionCastSpellRandom(SPELL_TASHAS_HIDEOUS_LAUGHTER, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeShortValid) + { + // Charm Person or Animal. Charms an animal, or humanoid, if fail mind will save. + if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + (GetIsPlayableRacialType(GlobalSpellTarget) || GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Charm Person or Animal. Level 2 (Druid) Charms an animal, or humanoid, if fail mind will save. + if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON_OR_ANIMAL, SpellHostRanged, i20, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; + } + } + } + + // Backup casting + if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Moderate wounds - damage at a touch attack. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i2 && RangeTouchValid && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Blackguard ability + if(AI_ActionUseFeatOnObject(FEAT_INFLICT_MODERATE_WOUNDS, GlobalMeleeTarget)) return TRUE; + + // Inflict Moderate Wounds. Level 2 (Cleric) Touch attack, hit means 2d8 + 1-10 negative damage. + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MODERATE_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE; + } + +/*::1111111111111111111111111111111111111111111111111111111111111111111111111111 + Level 1 Spells. +//::1111111111111111111111111111111111111111111111111111111111111111111111111111 + Thoughts - Not bad AOE spells, better at lower levels or with more people. + Magic missile is a decent spell for level 1, as it goes up to level 9 :-) and + is a classic. Others are simple enough, doing small amounts of damage or + doing some ability daamge, or sleep for limited hit dice creatures. Useful + AC enhancing spells at level 1 though! + + AOE: + [Burning Hands] - 1d4/level to 5d4 Reflex Fire damage to a cone AOE. + [Color Spray] - effect based on HD - Sleep Stun and Blindness if fail will save + [Grease] - Reflex save or knockdown, and slow in AOE. + [Sleep] - 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4. + [Entangle] - Entangles creatures in the AOE so they cannot move, reflex save, every round. +S [Bane] - -1 to all saves VS fear, attack rolls, damage. Opposite of Bless. + + Single: + [Charm Person] - Charms one humanoid if they fail a mind will save. + [Magic Missile] - 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9. + [Negative Energy Ray] 1d6(CasterLevel/2) to 5d6 negative damage. + [Doom] -2 Saves, Damage, to hit at a mind will save. + [Ray of Enfeeblement] - d6 + (Caster level/2) strength damage to d6 + 10. Fort save. + [Scare] - 1d4 round of fear, saving throw VS will, for 5HD or less creature +H [Horzilkaul's Boom] - 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness. +H [Ice Dagger] - Reflex-based, 1d4/Level (to 5d4) ice damage. + +S [Inflict Light Wounds] 1d8 + 1-5 negative damage on touch attack. + + Defender: + X [Endure Elements] 10/- Elemental resistance until 20 damage has been done + [Mage Armor] +1 Dodge/Armor/Deflection/Natural AC bonuses. (total +4) + [Protection From Alignment] +2 AC, mind immunity from alignment +S [Shield] +4 Armor AC and immunity to magic missile. +S [Shield of Faith] +2 deflection AC bonus, +1 every 6 levels (max +5) +S [Entropic Shield] 20% Consealment VS ranged attacks. +H X [Iron Guts] +4 Saves VS poison. (cast as level 0 spell...this is specilised!) + + Summon: + [Summon Creature I] - Summons a dire badger +H [Shelgarn's Persistant Blade] - Summons a dagger for the caster. + + Other: +SXXX[Amiplify] +20 to listen checks. +S X [Expeditious Retreat] - 150% movement speed. Cast before melee. +XXXX[Identify] - Extra lore. Not cast +S X [True Strike] +9 attack for a single attack. + X [Cure Light Wounds] 1d8 + 1-5 healing damage + X [Bless] +1 to attack rolls, damage for AOE, +2 VS fear saves. +S X [Divine Favor] +1/3 caster levels attack bonus, to +5. + X [Remove Fear] Removes fear! + X [Sanctuary] Will saving throw for enemies to spot you. Kinda a bad invisiblity. +SXXX[Camouflage] +10 hide for self. +S [Magic Fang] + 1 attack/damage to animal companion statistics. +H X [Magic Weapon] +1 enchantment to 1 weapon. +H X [Bless Weapon] +1 enchantment and +2d6 dam VS Undead to 1 weapon. +H X [Deafening Clang] +1 enchantment. +3 sonic damage. On Hit: Deafness, on a weapon. + +//::11111111111111111111111111111111111111111111111111111111111111111111111111*/ + + // Jump out if we don't want to cast level 1 spells. + if(iLowestSpellLevel > i1) return FALSE; + + // Ok, AC increasing/defensive spells first. + if(IsFirstRunThrough) + { + // Entropic shield. 20% VS ranged attackers - we need some ranged attackers + // OR people far away (After other AC spells). Also no normal consealment spells (they don't stack) + if(GlobalRangedAttackers && + !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells) && + !AI_GetAIHaveSpellsEffect(GlobalHasRangedConsealment) && + !AI_GetAIHaveEffect(GlobalEffectInvisible)) + { + // Entropic Shield. Level 1 (Cleric) 20% Consealment VS ranged attacks. + if(AI_ActionCastSpell(SPELL_ENTROPIC_SHIELD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + + // We don't cast AC spells which will not do well over other AC inducing spells + // - Shield + Divine Shield == Deflection. Never cast together + // - Barkskin, Stone Bones == Natural + // - Mage armor (+Epic) == Narual, Deflection, Dodge, Armor. Never cast with both Barkskin and Shield. + + // Out of the deflection spells, Shield first then Shield of Faith. + if(!AI_GetAIHaveSpellsEffect(GlobalHasDeflectionACSpell)) + { + // Shield. Level 1 (Mage) +4 Armor AC and immunity to magic missile. + if(AI_ActionCastSpell(SPELL_SHIELD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + // Shield of Faith. Level 1 (Cleric) +2 deflection AC bonus, +1 every 6 levels (max +5) + if(AI_ActionCastSpell(SPELL_SHIELD_OF_FAITH, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + // If we have not got natural or other AC, we cast mage armor. + if(!AI_GetAIHaveSpellsEffect(GlobalHasNaturalACSpell) && + !AI_GetAIHaveSpellsEffect(GlobalHasOtherACSpell)) + { + // Shadow conjuration version + if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_MAGE_ARMOR, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + // Mage Armor. Level 1 (MagE) +1 Dodge/Armor/Deflection/Natural AC bonuses. (total +4) + if(AI_ActionCastSpell(SPELL_MAGE_ARMOR, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + + // Cast Entropic shield if nearest enemy is over 4 M away. + if(!GlobalEnemiesIn4Meters && + !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells) && + !AI_GetAIHaveSpellsEffect(GlobalHasRangedConsealment) && + !AI_GetAIHaveEffect(GlobalEffectInvisible)) + { + // Entropic Shield. Level 1 (Cleric) 20% Consealment VS ranged attacks. + if(AI_ActionCastSpell(SPELL_ENTROPIC_SHIELD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + + // Cheat and use protection froms. Have to rely upon talents :-/ + // as cannot specify using ActionCast - it plain don't wanna work. + if(iEnemyAlignment == ALIGNMENT_GOOD && + !AI_GetAIHaveSpellsEffect(GlobalHasProtectionGoodSpell)) + { + // Protection From Alignment. Level 1 (Bard/Cleric/Paladin/Mage). +2 AC, mind immunity from alignment + if(AI_ActionCastSubSpell(SPELL_PROTECTION_FROM_GOOD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + else if(iEnemyAlignment == ALIGNMENT_EVIL && + !AI_GetAIHaveSpellsEffect(GlobalHasProtectionEvilSpell)) + { + if(AI_ActionCastSubSpell(SPELL_PROTECTION_FROM_EVIL, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE; + } + + // Ally buff spells if buffer + if(GlobalWeAreBuffer) + { + // Bless, Aid + if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_AID, SPELL_BLESS)) return TRUE; + } + } + + // BAB check + // - BAB checks check our BASE attack bonus, no modifiers. Basically, as + // normally even mages have a +X to attack, this provides a good indicator + // if we are going to easy, or very easily, hit the enemy. + // - Clerics, Druids and Bards must be able to hit even better then normal. + if(IsFirstRunThrough && !SRA && + GlobalOurChosenClass != CLASS_TYPE_WIZARD && + GlobalOurChosenClass != CLASS_TYPE_SORCERER && + GlobalOurChosenClass != CLASS_TYPE_FEY) + { + // If a druid, cleric, and so on, have to be able to hit better then + // more then normal + if(GlobalOurChosenClass == CLASS_TYPE_CLERIC || + GlobalOurChosenClass == CLASS_TYPE_DRUID || + GlobalOurChosenClass == CLASS_TYPE_BARD) + { + // BAB check for level 4 spells. 50% + if(GlobalOurBaseAttackBonus+ i10 >= GlobalMeleeTargetAC) return FALSE; + } + // Demons, fighters, anything else really. + else + { + // BAB check for level 4 spells. 25% + if(GlobalOurBaseAttackBonus + i15 >= GlobalMeleeTargetAC) return FALSE; + } + } + + // Try grenades - we always throw these. They are about level 1 standard of DC's + // and effects. Not too bad, when NPC's get a chance to use them! :-) + if(RangeMediumValid) + { + // - Note, these are also always thrown before melee, if the person has <5 HD. + // - Reasons for not casting are really the BAB checks. + if(AI_AttemptGrenadeThrowing(GlobalSpellTarget)) return TRUE; + } + + // Random hostile spell +// [Magic Missile] - 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9. +// [Negative Energy Ray] 1d6(CasterLevel/2) to 5d6 negative damage. +// [Scare] - 1d4 round of fear, saving throw VS will, for 5HD or less creature +// [Ray of Enfeeblement] - d6 + (Caster level/2) strength damage to d6 + 10. Fort save. +// [Doom] -2 Saves, Damage, to hit at a mind will save. +// [Charm Person] - Charms one humanoid if they fail a mind will save. +// [Horzilkaul's Boom] - 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness. +// [Ice Dagger] - Reflex-based, 1d4/Level (to 5d4) ice damage. + + // Is it best to target with single-target spells first? + // Magic missile, negative energy ray and flame lash are better at damaging + // one target, if there is one target, then the AOE spells usually. + // 60-70% if favourable. + // - We don't cast inflict light wounds. GetHasSpell bodges with spontaeous spells. + if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i1) + { + // Magic Missile - Is a nice long range spell. Can do damage to almost anything. + if(RangeLongValid && !GetHasSpellEffect(SPELL_SHIELD, GlobalSpellTarget)) + { + if(d10() <= i6) + { + // Shad. conjuration + if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_MAGIC_MISSILE, SpellHostRanged, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Magic Missile. Level 1 (Mage) 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9. + if(AI_ActionCastSpellRandom(SPELL_MAGIC_MISSILE, SpellHostRanged, i60, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Horizikaul's boom is sonic damage - no save! Also, goes also to 5d4 (at level 10 anyway). Comparable to MM! + if(RangeShortValid) + { + // Horzilkaul's Boom. Level 1 (Mage) 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness. + if(AI_ActionCastSpellRandom(SPELL_HORIZIKAULS_BOOM, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + if(RangeMediumValid) + { + // Negative energy ray is similar to Magic Missile, but heals undead. can do more dmage then MM, but more random - d6 compared to d4 + 1. + if(GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD && + GlobalSpellTargetRace != RACIAL_TYPE_CONSTRUCT && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Negative Energy Ray. Level 1 (Mage) 2 (Cleric) 1d6(CasterLevel/2) to 5d6 negative damage. + if(AI_ActionCastSpellRandom(SPELL_NEGATIVE_ENERGY_RAY, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Doom does some...stuff. The damage -2 is best. Will save negates. + if(!GetHasSpellEffect(SPELL_DOOM, GlobalSpellTarget) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1)) + { + // Doom. Level 1 (Mage) -2 Saves, Damage, to hit at a mind will save. + if(AI_ActionCastSpellRandom(SPELL_DOOM, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeShortValid) + { + // Damage with Ice Dagger is not bad - it is cirtinly comparable to MM! Saves though... + if(!AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i1)) + { + // Ice Dagger. Level 1 (Mage) Reflex-based, 1d4/Level (to 5d4) ice damage. + if(AI_ActionCastSpellRandom(SPELL_ICE_DAGGER, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Scare - needs 5HD or under HD + if(!AI_GetSpellTargetImmunity(GlobalImmunityFear) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1)) + { + // Scare. Level 1 (Mage) 1d4 round of fear, saving throw VS will, for 5HD or less creature + if(AI_ActionCastSpellRandom(SPELL_SCARE, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Ray of Enfeeblement pities high strength enemies. d6 + 1-10 str. Loss + if(GetAbilityScore(GlobalSpellTarget, ABILITY_STRENGTH) >= i14 && + !GetHasSpellEffect(SPELL_RAY_OF_ENFEEBLEMENT, GlobalSpellTarget) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i1)) + { + // Ray of Enfeeblement. Level 1 (Mage) d6 + (Caster level/2) strength damage to d6 + 10. Fort save. + if(AI_ActionCastSpellRandom(SPELL_RAY_OF_ENFEEBLEMENT, SpellHostRanged, i40, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Charm Person charms them. Might as well... (Bit bad this spell) + if(GetIsPlayableRacialType(GlobalSpellTarget) && + !AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Charm Person. Level 1 (Mage/Bard) Charms one humanoid if they fail a mind will save. + if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + } + // Single spell override backup casting + if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + + // Random AOE spell. +// [Burning Hands] - 1d4/level to 5d4 Reflex Fire damage to a cone AOE. +// [Color Spray] - effect based on HD - Sleep Stun and Blindness if fail will save +// [Grease] - Reflex save or knockdown, and slow in AOE. +// [Sleep] - 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4. +// [Bane] - -1 to all saves VS fear, attack rolls, damage. Opposite of Bless. +// [Entangle] - Entangles creatures in the AOE so they cannot move, reflex save, every round. + + // Burning hands - fire reflex damage to a cone + if(SpellHostAreaInd && RangeShortValid && + (GetHasSpell(SPELL_BURNING_HANDS) || ItemHostAreaInd == SPELL_BURNING_HANDS)) + { + // Cone AOE, short ranged, reflex based save. Reaction friendly. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, f10, i1, SAVING_THROW_REFLEX, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly); + // Is it valid? 70% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Burning Hands. Level 1 (Mage) 1d4/level to 5d4 Reflex Fire damage to a cone AOE. + if(AI_ActionCastSpellRandom(SPELL_BURNING_HANDS, SpellHostAreaInd, i60, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Color Spray - Does at least blind higher levels. Will save, however, negates + if(SpellHostAreaInd && RangeShortValid && + (GetHasSpell(SPELL_COLOR_SPRAY) || ItemHostAreaInd == SPELL_COLOR_SPRAY)) + { + // Cone AOE, short ranged, reflex based save. Reaction friendly. + oAOE = AI_GetBestAreaSpellTarget(fShortRange, f10, i1, SAVING_THROW_WILL, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly); + // Is it valid? 70% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Color Spray. Level 1 (Mage) - effect based on HD - Sleep Stun and Blindness if fail will save + if(AI_ActionCastSpellRandom(SPELL_COLOR_SPRAY, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Grease means knockdown - good if they are not immune to knockdown :-P (reflex save) + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_GREASE) || ItemHostAreaInd == SPELL_GREASE)) + { + // Take as Medium AOE, long ranged, reflex based save. Reaction friendly. + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_MEDIUM, i1, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Grease. Level 1 (Mage) Reflex save or knockdown, and slow in AOE. + if(AI_ActionCastSpellRandom(SPELL_GREASE, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Sleep - 8HD or less average HD for enemy to target this. + if(SpellHostAreaInd && RangeMediumValid && GlobalAverageEnemyHD <= i8 && + (GetHasSpell(SPELL_SLEEP) || GetHasFeat(FEAT_HARPER_SLEEP) || ItemHostAreaInd == SPELL_SLEEP)) + { + // Huge AOE, medium ranged, will based save. Reaction type enemy + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i1, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireHostile); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // The harper sleep + if(AI_ActionUseFeatOnObject(FEAT_HARPER_SLEEP, oAOE)) return TRUE; + + // Sleep. Level 1 (Mage) 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4. + if(AI_ActionCastSpellRandom(SPELL_SLEEP, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE; + } + } + // Bane is...alright, I guess. Will save, however. It doesn't affect allies though! + if(SpellEnhSinTar && RangeLongValid && + (GetHasSpell(SPELL_BANE) || ItemEnhSinTar == SPELL_BANE)) + { + // collosal AOE, long ranged, will based save. No allies + oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_COLOSSAL, i1, SAVING_THROW_WILL); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Sleep. Level 1 (Mage) 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4. + if(AI_ActionCastSpellRandom(SPELL_BANE, SpellEnhSinTar, i50, oAOE, i11, TRUE, ItemEnhSinTar)) return TRUE; + } + } + // Entangle stops them moving - not a bad spell to say the least, I guess + if(SpellHostAreaInd && RangeLongValid && + (GetHasSpell(SPELL_ENTANGLE) || ItemHostAreaInd == SPELL_ENTANGLE)) + { + // Huge AOE, Long ranged, reflex based save. Reaction type friendly + oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i1, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly); + // Is it valid? 60% chance of casting. + if(GetIsObjectValid(oAOE)) + { + // Entangle. Level 1 (Druid/Ranger) Entangles creatures in the AOE so they cannot move, reflex save, every round. + if(AI_ActionCastSpellRandom(SPELL_ENTANGLE, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE; + } + } + + // Multi spell override backup casting + if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Level 1 summons + if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i1 && GlobalOurHitDice <= i10 && + (GlobalOurHitDice <= i6 || GlobalMeleeAttackers <= i2)) + { + // Shelgarn's Persistant Blade. Level 1 (Mage only) Summons a dagger for the caster. + if(AI_ActionCastSummonSpell(SPELL_SHELGARNS_PERSISTENT_BLADE, i11, i1)) return TRUE; + // Summon Monster I (1). Level 1 (Most classes) Summons a Dire Badger. + if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_I, i11, i1)) return TRUE; + } + + // Single spells again + // Similar %'s as it is level 1 spells. + // - We don't cast inflict light wounds. GetHasSpell bodges with spontaeous spells. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i1) + { + // Magic Missile - Is a nice long range spell. Can do damage to almost anything. + if(RangeLongValid && !GetHasSpellEffect(SPELL_SHIELD, GlobalSpellTarget)) + { + // Shad. conjuration + if(d10() <= i4) + { + if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_MAGIC_MISSILE, SpellHostRanged, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Magic Missile. Level 1 (Mage) 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9. + if(AI_ActionCastSpellRandom(SPELL_MAGIC_MISSILE, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Horizikaul's boom is sonic damage - no save! Also, goes also to 5d4 (at level 10 anyway). Comparable to MM! + if(RangeShortValid) + { + // Horzilkaul's Boom. Level 1 (Mage) 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness. + if(AI_ActionCastSpellRandom(SPELL_HORIZIKAULS_BOOM, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + if(RangeMediumValid) + { + // Negative energy ray is similar to Magic Missile, but heals undead. can do more dmage then MM, but more random - d6 compared to d4 + 1. + if(GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD && + GlobalSpellTargetRace != RACIAL_TYPE_CONSTRUCT && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Negative Energy Ray. Level 1 (Mage) 2 (Cleric) 1d6(CasterLevel/2) to 5d6 negative damage. + if(AI_ActionCastSpellRandom(SPELL_NEGATIVE_ENERGY_RAY, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Doom does some...stuff. The damage -2 is best. Will save negates. + if(!GetHasSpellEffect(SPELL_DOOM, GlobalSpellTarget) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1)) + { + // Doom. Level 1 (Mage) -2 Saves, Damage, to hit at a mind will save. + if(AI_ActionCastSpellRandom(SPELL_DOOM, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + } + if(RangeShortValid) + { + // Damage with Ice Dagger is not bad - it is cirtinly comparable to MM! Saves though... + if(!AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i1)) + { + // Ice Dagger. Level 1 (Mage) Reflex-based, 1d4/Level (to 5d4) ice damage. + if(AI_ActionCastSpellRandom(SPELL_ICE_DAGGER, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Scare - needs 5HD or under HD + if(!AI_GetSpellTargetImmunity(GlobalImmunityFear) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1)) + { + // Scare. Level 1 (Mage) 1d4 round of fear, saving throw VS will, for 5HD or less creature + if(AI_ActionCastSpellRandom(SPELL_SCARE, SpellHostRanged, i20, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Ray of Enfeeblement pities high strength enemies. d6 + 1-10 str. Loss + if(GetAbilityScore(GlobalSpellTarget, ABILITY_STRENGTH) >= i14 && + !GetHasSpellEffect(SPELL_RAY_OF_ENFEEBLEMENT, GlobalSpellTarget) && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i1)) + { + // Ray of Enfeeblement. Level 1 (Mage) d6 + (Caster level/2) strength damage to d6 + 10. Fort save. + if(AI_ActionCastSpellRandom(SPELL_RAY_OF_ENFEEBLEMENT, SpellHostRanged, i20, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + // Charm Person charms them. Might as well... (Bit bad this spell) + if(GetIsPlayableRacialType(GlobalSpellTarget) && + !AI_GetSpellTargetImmunity(GlobalImmunityDomination) && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2)) + { + // Charm Person. Level 1 (Mage/Bard) Charms one humanoid if they fail a mind will save. + if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON, SpellHostRanged, i10, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; + } + } + } + + // Backup casting + if(AI_ActionCastBackupRandomSpell()) return TRUE; + + // Light wounds - damage at a touch attack. + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i1 && RangeTouchValid && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Blackguard ability. + if(AI_ActionUseFeatOnObject(FEAT_INFLICT_LIGHT_WOUNDS, GlobalMeleeTarget)) return TRUE; + + // Inflict Light Wounds. Level 1 (Cleric) Touch attack, hit means 1d8 + 1-5 negative damage. + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_LIGHT_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE; + } + + // Lastly, cheat cast spells. We cast these if we run out of all others + // - Cast ABOVE level 0 spells. + if(GetAIConstant(AI_CHEAT_CAST_SPELL + s1) >= FALSE) + { + // It is normally 6 spells. It will default to the first if one + // we pick is not valid. + int iSpell = GetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(d6())); + if(iSpell <= i0) + { + iSpell = GetAIConstant(AI_CHEAT_CAST_SPELL + s1); + } + // 33: "[DCR:Casting] Cheat Spell. End of Spells. [Spell] " + IntToString(iSpell) + "[Target]" + GetName(GlobalSpellTarget) + DebugActionSpeakByInt(33, GlobalSpellTarget, iSpell); + ActionCastSpellAtObject(iSpell, GlobalSpellTarget, METAMAGIC_NONE, TRUE); + return TRUE; + } + +/*::0000000000000000000000000000000000000000000000000000000000000000000000000000 + Level 0 Spells. +//::0000000000000000000000000000000000000000000000000000000000000000000000000000 + Thoughts - These are backup spells, even at level 1. To be honest, ray + of frost at level 1-3 is a good spell, even 5+ it is alright, as it has + no save. (Shame it isn't like Acid Splash, as in correct D&D damage). + Daze is also useful in some situations. No AOE spells and the defensive + spells are nothing to speak of, however. + + AOE: + X [None] + + Single: + [Ray of frost] 1d4 + 1 cold damage, at no save. +S [Flare] Target gets -1 to attack rolls if they fail a fortitude save + [Daze] If <= 5 Hit dice, target is dazed on a failed fortitude save. +S [Acid Splash] 1d3 Acid damage, no save. Longer range then ray of frost. +S [Electric Jolt] 1d3 Electrical damage, no save. Longer range then ray of frost. +S [Inflict Minor Wounds] 1 Negative damage, on a touch attack. Heals undead. + + Defender: + [Resistance] +1 to all saves for 2 turns + [Virtue] +1 HP, 1 turn/caster level. + + Summon: + X [None] + + Other: + X [Cure Minor Wounds] - Heals 4 hit points (meant to be only 1) + [Light] 20M of light around the target. +//::00000000000000000000000000000000000000000000000000000000000000000000000000*/ + + // Jump out if we don't want to cast level 0 spells. + if(iLowestSpellLevel > i0) return FALSE; + + // BAB check. + if(!SRA && GlobalOurChosenClass != CLASS_TYPE_WIZARD && + GlobalOurChosenClass != CLASS_TYPE_SORCERER && + GlobalOurChosenClass != CLASS_TYPE_FEY) + { + // BAB check for level 0 spells. + if(GlobalOurBaseAttackBonus >= GetAC(GlobalSpellTarget)) return FALSE; + // HD check + if(GlobalOurHitDice > i8) return FALSE; + } + + if(IsFirstRunThrough) + { + // Need no enemies in 4 meters. + if(!GlobalEnemiesIn4Meters) + { + // Not sure of effectiveness...so acting as a cantrip. + // Sanctuary. Level 1 (Cleric). Will save (low DC!) or cannot see target. + if(AI_ActionCastSpell(SPELL_SANCTUARY, SpellEnhSinTar, OBJECT_SELF, i11, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + } + // Iron guts are very specifically VS poisoners - I'd see the use if I saw + // spiders. I won't bother adding them checks, however, because frankly NPC's + // won't use this much + if(!GetHasSpellEffect(SPELL_IRONGUTS)) + { + // Iron Guts. Level 1 (Mage) +4 Saves VS poison. (cast as level 0 spell...this is specilised!) + if(AI_ActionCastSpell(SPELL_IRONGUTS, SpellEnhSinTar, OBJECT_SELF, i11, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; + } + } + + // Note: Also, as these are always the worst, last things to use in combat, we + // don't care about ranges :-P + // Note 2: We have the required amount of stat to 0. We check, On Spawn, + // if we can cast spells (IE right stats) and these are the worst we can cast. + if(GlobalNormalSpellsNoEffectLevel < i1 && GlobalSeenSpell) + { + // Random cast one of the 5 hostile cantrip spells + // Daze! + if(RangeLongValid && + GlobalSpellTargetHitDice <= i5 && + !AI_GetSpellTargetImmunity(GlobalImmunityMind) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i0)) + { + // Daze. Level 0 (Mage/Bard) If <= 5 Hit dice, target is dazed on a failed fortitude save. + if(AI_ActionCastSpellRandom(SPELL_DAZE, SpellHostRanged, i50, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE; + } + if(RangeMediumValid) + { + // Minor damage. Moderate range. 60% chance of casting. + if(AI_ActionCastSpellRandom(SPELL_RAY_OF_FROST, SpellHostRanged, i50, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE; + } + if(RangeLongValid) + { + // Long range, for Acid Splash and Electric Jolt. Random cast one of them + if(AI_ActionCastSpellRandom(SPELL_ELECTRIC_JOLT, SpellHostRanged, i40, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE; + if(AI_ActionCastSpellRandom(SPELL_ACID_SPLASH, SpellHostRanged, i40, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE; + } + // Flare is OK - low 10% cast, but will backup cast at end. + if(RangeMediumValid && !GetHasSpellEffect(SPELL_FLARE, GlobalSpellTarget) && + !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i0)) + { + // Flare. Level 0 (Bard/Mage) Target gets -1 to attack rolls if they fail a fortitude save + if(AI_ActionCastSpellRandom(SPELL_FLARE, SpellHostRanged, i0, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE; + } + + // Backup casting + if(AI_ActionCastBackupRandomSpell()) return TRUE; + } + // Need decent % HP and no enemies in 4 Meters to cast these + if(IsFirstRunThrough && GlobalOurPercentHP >= i60 && !GlobalEnemiesIn4Meters) + { + if(!GetHasSpellEffect(SPELL_VIRTUE)) + { + // Virtue. Level 0 (Druid/Cleric/Paladin) +1 HP, 1 turn/caster level. + if(AI_ActionCastSpell(SPELL_VIRTUE, SpellEnhSinTar, OBJECT_SELF, i10, FALSE, ItemEnhSinTar)) return TRUE; + } + if(!GetHasSpellEffect(SPELL_RESISTANCE)) + { + // Resistance. Level 0 (Mage/Cleric/Bard/Druid) 1 (Paladin) +1 to all saves for 2 turns + if(AI_ActionCastSpell(SPELL_RESISTANCE, SpellEnhSinTar, OBJECT_SELF, i10, FALSE, ItemEnhSinTar)) return TRUE; + } + // Light + if(!GetHasSpellEffect(SPELL_LIGHT)) + { + // Light. Level 0 (Mage/Cleric/Druid/Bard) 20M of light around the target. + if(AI_ActionCastSpell(SPELL_LIGHT, SpellOtherSpell, OBJECT_SELF, i10)) return TRUE; + } + } + // Minor wounds - damage at a touch attack. + // HARDLY worth it, but use it anyway...if we pass the BAB check that is :-) + if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i1 && RangeTouchValid && + !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && + !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) + { + // Inflict Minor Wounds. Level 0 (Cleric) Touch attack, hit means 1 negative damage. + if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MINOR_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE; + } + + // This is when it may loop back, after moving forward a bit + // Ranges: 20, 8, and 2.25. Long range spells are always cast. + if(SRA) + { + // We have integers, set to TRUE or FALSE. + // - 2 sets, one that was passed from last time, one which we + // just used. + + // Just used = RangeLongValid. TRUE = CASTED + // New ones = InputRangeLongValid. TRUE = DO NOT CAST. + + // So, we toggle the RangeLongValid's, and if they are TRUE, we + // put that into the next loop. If it was FALSE, we check + // the InputRangeLongValid to see if we checked it before. + + // If false, set the value to the Input value. + if(RangeLongValid == FALSE) + { + RangeLongValid = InputRangeLongValid; + } + if(RangeMediumValid == FALSE) + { + RangeMediumValid = InputRangeMediumValid; + } + if(RangeShortValid == FALSE) + { + RangeShortValid = InputRangeShortValid; + } + if(RangeTouchValid == FALSE) + { + RangeTouchValid = InputRangeTouchValid; + } + // Do a new loop. Took out the ones we have already done :-) + // 34: "[DCR: All Spells] Ranged Spells. Should use closer spells/move nearer" + DebugActionSpeakByInt(34); + // We go through, reducing amount by 1. + if(AI_AttemptAllSpells(iLowestSpellLevel, iBABCheckHighestLevel, iLastCheckedRange + i1, RangeLongValid, RangeMediumValid, RangeShortValid, RangeTouchValid)) return TRUE; + } + // Return false. No spell cast. + return FALSE; +} + + +/*:://///////////////////////////////////////////// +//:: Name ActionDragonBreath +//:://///////////////////////////////////////////// + Wrapper to use dragon breath. TRUE if: + 1. They don't have the spell's effects (unless iDamaging is TRUE) + 2. We have it! + + After it is used, iWingCounter is set, debug message, and re-set counter to 0. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//:://///////////////////////////////////////////*/ + +// Either the chosen class is a dragon, or the appearance type is a dragon type, +// we return TRUE. +int AI_GetIsDragon() +{ + // Basic check + if(GlobalOurChosenClass == CLASS_TYPE_DRAGON) + { + return TRUE; + } + // Appearance type (includes if we polymorph into one!) + switch(GlobalOurAppearance) + { + case APPEARANCE_TYPE_DRAGON_BLACK: + case APPEARANCE_TYPE_DRAGON_BLUE: + case APPEARANCE_TYPE_DRAGON_BRASS: + case APPEARANCE_TYPE_DRAGON_BRONZE: + case APPEARANCE_TYPE_DRAGON_COPPER: + case APPEARANCE_TYPE_DRAGON_GOLD: + case APPEARANCE_TYPE_DRAGON_GREEN: + case APPEARANCE_TYPE_DRAGON_RED: + case APPEARANCE_TYPE_DRAGON_SILVER: + case APPEARANCE_TYPE_DRAGON_WHITE: + // Sorta dragons + case APPEARANCE_TYPE_FAERIE_DRAGON: + case APPEARANCE_TYPE_PSEUDODRAGON: + // Tiny ones! + case APPEARANCE_TYPE_WYRMLING_BLACK: + case APPEARANCE_TYPE_WYRMLING_BLUE: + case APPEARANCE_TYPE_WYRMLING_BRASS: + case APPEARANCE_TYPE_WYRMLING_BRONZE: + case APPEARANCE_TYPE_WYRMLING_COPPER: + case APPEARANCE_TYPE_WYRMLING_GOLD: + case APPEARANCE_TYPE_WYRMLING_GREEN: + case APPEARANCE_TYPE_WYRMLING_RED: + case APPEARANCE_TYPE_WYRMLING_SILVER: + case APPEARANCE_TYPE_WYRMLING_WHITE: + // Hordes ones + case 425: // Dragon_Pris + case 418: // Dragon_Shadow + case 405: // Dracolich + return TRUE; + break; + } + return FALSE; +} + +// Uses tBreath if they are not immune +// - TRUE if used. +int AI_ActionUseBreath(object oTarget, talent tBreath, int iSpellID) +{ + int iImmune = FALSE;// If TRUE, don't use it it + // Go through them... + switch(iSpellID) + { + case SPELLABILITY_DRAGON_BREATH_FEAR: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_FEAR); break; + case SPELLABILITY_DRAGON_BREATH_PARALYZE: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_PARALYSIS); break; + case SPELLABILITY_DRAGON_BREATH_SLEEP: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_SLEEP); break; + case SPELLABILITY_DRAGON_BREATH_SLOW: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_SLOW); break; + case SPELLABILITY_DRAGON_BREATH_WEAKEN: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_ABILITY_DECREASE); break; + } + // Use it! + if(!iImmune) + { + // 35: "[DCR:Dragon] Breath weapon & attacking [Breath ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + DebugActionSpeakByInt(35, oTarget, iSpellID); + ActionUseTalentAtLocation(tBreath, GetLocation(oTarget)); + ActionAttack(oTarget); + return TRUE; + } + return FALSE; +} +int AI_ActionDragonBreath(object oTarget, int iWingCounter) +{ + // Get a random breath... + talent tBreath = GetCreatureTalentRandom(TALENT_CATEGORY_DRAGONS_BREATH); + if(GetIsTalentValid(tBreath)) + { + int iTypeRandom, iTypeBest; + // Check if it affects them + iTypeRandom = GetIdFromTalent(tBreath); + if(!GetHasSpellEffect(iTypeRandom, oTarget)) + { + if(AI_ActionUseBreath(oTarget, tBreath, iTypeRandom)) return TRUE; + } + else + { + // Try again...best this time + tBreath = GetCreatureTalentBest(TALENT_CATEGORY_DRAGONS_BREATH, i20); + if(GetIsTalentValid(tBreath)) + { + iTypeBest = GetIdFromTalent(tBreath); + if(iTypeBest != iTypeRandom && !GetHasSpellEffect(iTypeBest, oTarget)) + { + if(AI_ActionUseBreath(oTarget, tBreath, iTypeBest)) return TRUE; + } + } + } + } + return FALSE; +} +int AI_DragonBreathOrWing(object oTarget) +{ + // We may re-set SpellHostBreath if invalid, and we are polymorphed + if(SpellHostBreath == FALSE && AI_GetAIHaveEffect(GlobalEffectPolymorph)) + { + SpellHostBreath = GetIsTalentValid(GetCreatureTalentBest(TALENT_CATEGORY_DRAGONS_BREATH, MAXCR)); + } + + // Breath attack, or wing buffet. Which one?! + // Check if we can do either...and that we are huge - IE not persuado dragon! + if(SpellHostBreath || GlobalOurSize >= CREATURE_SIZE_HUGE) + { + // Adds one to all things, by default, every call. This will randomise when to use things. + int nBreath = GetAIInteger(AI_DRAGONS_BREATH); + int nWing = GetAIInteger(AI_WING_BUFFET); + int iAboveXBreath = GetBoundriedAIInteger(AI_DRAGON_FREQUENCY_OF_BUFFET, i3); + int iAboveXWing = GetBoundriedAIInteger(AI_DRAGON_FREQUENCY_OF_BREATH, i3); + // There is a small chance of actuall reducing it for one after it, or + // adding another! + if(d20() == i1 && (nWing > FALSE || nBreath > FALSE))// 5% chance of x2 the number! + { + nWing *= i2; + nBreath *= i2; + } + else if(d20() == i1)// 5% of reducing each by 1. + { + nWing--; + nBreath--; + } + else // Else we add 1 as normal + { + // Add one... + nWing++; + nBreath++; + } + // Set normal values to locals again + SetAIInteger(AI_WING_BUFFET, nWing); + SetAIInteger(AI_DRAGONS_BREATH, nBreath); + // Check 1. If breath is over 2 of wing, we may use it. + if(!GetSpawnInCondition(AI_FLAG_COMBAT_NO_WING_BUFFET, AI_COMBAT_MASTER) && SpellHostBreath && + nBreath >= iAboveXBreath && nBreath >= (nWing + i2)) + { + // We don't attack, with breath, our own dragons (IE as 3E rules, and + // the factmost dragons do have that immunity to that damage in place) + if(GetAppearanceType(oTarget) != GlobalOurAppearance) + { + if(AI_ActionDragonBreath(oTarget, nWing)) + { + SetAIInteger(AI_DRAGONS_BREATH, i0); + return TRUE; + } + } + } + // Else wing must be higher, or no breath! + // So we use wing buffet, re-set that, then try breath to end... + if(nWing >= iAboveXWing && GlobalOurSize >= CREATURE_SIZE_HUGE && + GetCreatureSize(oTarget) < CREATURE_SIZE_HUGE) + { + // 36: "[DCR:Dragon] Wing Buffet [Target] " + GetName(oTarget) + DebugActionSpeakByInt(36, oTarget); + // Reset wing buffet counter + SetAIInteger(AI_WING_BUFFET, i0); + SetAIInteger(AI_DRAGONS_BREATH, nBreath); + // - Not action do command, just Execute Script + ExecuteScript(FILE_DRAGON_WING_BUFFET, OBJECT_SELF); + return TRUE; + } + // Breath final... + if(SpellHostBreath && nBreath >= iAboveXBreath) + { + // Breaths. + if(AI_ActionDragonBreath(oTarget, nWing)) + { + SetAIInteger(AI_DRAGONS_BREATH, i0); + return TRUE; + } + } + }//End any check + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name TalentDragonCombat +//:://///////////////////////////////////////////// + Main call for dragons. This will cast major spells, + use feats, wing buffet and breath weapons. +//:://///////////////////////////////////////////// +//:: Created By: Bioware. Heavily Modified: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptDragonCombat() +{ + // We will attempt all spells possible with SRA on + if(SRA) + { + if(AI_AttemptAllSpells()) return TRUE; + } + + // Now, we use DRAGONS BREATH! MUHAHAHAHAH! + // OR wing buffet! Yeehaw! + // - This is done on a breath by breath basis, with GetAppearance checking! + // - Basically, these are as powerful (if not more so) then level 9 spells + if(AI_DragonBreathOrWing(GlobalMeleeTarget)) return TRUE; + + // Chance each round to use best spells possible. + // We, always, love level 9 spells! just great, especially if the dragon has them! + if(AI_AttemptAllSpells(i9)) return TRUE; + + // We may attack our GlobalMeleeTarget if they are very weak in the AC + // department! + // - We will always hit + // - They have no DR protections + // - They have under 50 current hit points + // - They are in HTH combat range. + if(GlobalOurBaseAttackBonus - i5 >= GlobalMeleeTargetAC && + !AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections, GlobalMeleeTarget) && + !AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections, GlobalMeleeTarget) && + GetCurrentHitPoints(GlobalMeleeTarget) < i50 && GlobalRangeToMeleeTarget < f4) + { + // We then attack with feats, or whatever :-) + // - This includes flying + AI_AttemptMeleeAttackWrapper(); + return TRUE; + } + // We will use more spells if there are more enemies - as AOE spells will + // be *maybe* better to use. + int iSpellLowestMinus; + if(GlobalMeleeAttackers > i4 || GlobalTotalSeenHeardEnemies > i6) + { + // -6 + iSpellLowestMinus = i6; + } + else + { + // -2d4 + iSpellLowestMinus = d4(i2); + } + // We randomly choose what to use... + if(AI_AttemptAllSpells(i9 - iSpellLowestMinus, i0 + Random(i6))) + { + // We also ActionAttack the enemy, as to move closer :-) + ActionAttack(GlobalMeleeTarget); + return TRUE; + } + + // We then attack with feats, or whatever :-) + // - This includes flying + return AI_AttemptMeleeAttackWrapper(); +} +// Beholder teleport attempt. Flees from combat. +int AI_ActionBeholderTeleport() +{ + // Go from futhest to nearest seen allies + int iCnt = GetLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_ALLIES_RANGE_SEEN); + if(iCnt <= FALSE) return FALSE; + int iBreak = FALSE; + object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oEnemy) || GetDistanceToObject(oEnemy) > f5) return FALSE; + // Loop futhest to nearest + object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oAlly) && iBreak != TRUE) + { + // Check nearest enemy to the ally. + oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oAlly, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + if(GetDistanceToObject(oEnemy) > f5) + { + iBreak = TRUE; + } + else + { + // Next futhest. + iCnt--; + oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + } + } + // If true, we run to the ally + if(iBreak) + { + // 37: "[DCR] Beholder Teleport" + DebugActionSpeakByInt(37); + effect eBeholder = EffectDisappearAppear(GetLocation(oAlly)); + float fTime = f3 + (GetDistanceToObject(oAlly)/f10); + // Apply effect + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeholder, OBJECT_SELF, fTime); + // Determine Combat Round + DelayCommand(fTime + f1, DetermineCombatRound(GlobalMeleeTarget)); + } + return FALSE; +} +// Beholders have special rays for eyes. This is used if the setting is set +// on spawn for beholders, or if the appearance is a beholder. +int AI_AttemptBeholderCombat() +{ + // We will randomly teleport away if low HP, then heal + if(GlobalOurPercentHP < GetBoundriedAIInteger(AI_HEALING_US_PERCENT, i50, i100, i1)) + { + // Randomly teleport directly to an ally with no one near to them. + if(AI_ActionBeholderTeleport()) + { + return TRUE; + } + // Else attempt to heal because we are far enough away. + else if(AI_AttemptHealingSelf()) + { + return TRUE; + } + } + + // We will attempt high-level spells first. + // - This will also make them use protections of course + // - Only level 9 and 8 spells. + if(AI_AttemptAllSpells(i8)) return TRUE; + + // We attempt to fire beholder rays, or do antimagic cone. + // 736 Beholder_Special_Spell_AI - Handles Beholder rays + // 727 Beholder_Anti_Magic_Cone - Dispels all Magical effects, and... + /* Beholder anti magic cone + 30m cone, + 100% spell failure to all targets, + 100% spellresistance to all targets + 9 seconds duration + No save */ + + // 80% chance of rays if at range (must be seen!) + if(d100() <= i80 && GlobalSeenSpell) + { + // 38: "[DCR] Beholder Rays" + DebugActionSpeakByInt(38); + ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_ALLRAYS, GlobalSpellTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + return TRUE; + } + // Then magic or bust - no BAB checks + else if(AI_AttemptAllSpells(FALSE, FALSE)) + { + return TRUE; + } + // Else there is a 20% chance of melee attack if enemy is at low HP + else if(GetCurrentHitPoints(GlobalMeleeTarget) <= i30 && + GlobalMeleeTargetAC <= i25 && + GlobalRangeToMeleeTarget < f5 && d100() <= i20) + { + if(AI_AttemptMeleeAttackWrapper()) + { + return TRUE; + } + else + { + // 38: "[DCR] Beholder Rays" + DebugActionSpeakByInt(38); + ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_ALLRAYS, GlobalSpellTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + return TRUE; + } + } + // Then rays finally + else + { + // 38: "[DCR] Beholder Rays" + DebugActionSpeakByInt(38); + ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_ALLRAYS, GlobalSpellTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + return TRUE; + } + // Should never return FALSE. + return FALSE; +} +// Taken from x2_ai_mflayer. +// Bioware's implimenation of the Mind Suck. +void MindFlayerSuck(object oTarget) +{ + if(GetDistanceBetween(OBJECT_SELF, oTarget) < 1.5) + { + ActionMoveAwayFromObject(oTarget, FALSE, 1.5); + } + ActionMoveToObject(oTarget, FALSE, 1.5); + ActionDoCommand(SetFacingPoint(GetPosition(oTarget))); + ActionWait(0.5); + // normal brain suck + ActionCastSpellAtObject(AI_SPELLABILITY_SUCKBRAIN, oTarget,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT); +} +// Illithid Use special attacks. +// This is set on spawn, or by the user on spawn. +int AI_AttemptMindflayerCombat() +{ + // We may attempt a brain suck as Bioware's mind flayer would. + + // - Mind attack if the melee enemy is NOT polymorphed, + // but is stunned/paralyzed or Dazed + % chance + + // Check polymorph + if(AI_GetAIHaveEffect(GlobalEffectPolymorph, GlobalMeleeTarget)) return FALSE; + + // Stunned, held, uncommandable, it is 100% + if((AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalMeleeTarget) || + AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalMeleeTarget)) || + // 30% with daze only. + (AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalMeleeTarget) && d100() <= i30)) + { + // Bioware function. + MindFlayerSuck(GlobalMeleeTarget); + return TRUE; + } + // 70% chance of mind blast + else if(GetDistanceToObject(GlobalMeleeTarget) < f5 && d100() <= i70) + { + // Special mind blast + ActionCastSpellAtObject(551, OBJECT_SELF,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT); + } + // False means no special mind flayer attacks. + return FALSE; +} + + +// Sets a value, 1-5, for what we can Dispel. Also sets +// a 1-5 value for breach spells. +// The values are NOT, I repeat NOT what the spell level are. Generally, they +// class spell-stopping spells as a higher prioritory to Dispel! +// * 5 - Dispeled before hostile spells are cast at target +// * 4 - Dispeled just before level 7 or so spells. +// * 3 - Dispeled just before level 4 or so spells +// * 2 - Dispeled just before level 2 or so spells. +// * 1 - Lowest prioritory - Dispeled at the end. +// There are NO cantrips included (level 0 spells). +void AI_SetDispelableEnchantments() +{ + // We CANNOT dispel any of the epic spells, so we DO NOT check for them + // here!!! + // Note that we do not dispel GlobalDispelTarget if they are stopped + // somehow. + + // Summons dispelling + object oMaster = GetMaster(GlobalDispelTarget); + if(GetIsObjectValid(oMaster)) + { + // First, is it the black blade we can dispel? (If cast aganst the master) + if(GetTag(GlobalDispelTarget) == "x2_s_bblade") + { + // Check if seen + if(GetObjectSeen(oMaster) || GetObjectHeard(oMaster)) + { + // Dispel the caster + GlobalDispelTarget = oMaster; + GlobalDispelTargetHighestDispel = i5; + return; + } + else + { + // Else, not seen or heard, so we try and dispel just the sword + GlobalDispelTargetHighestDispel = i5; + return; + } + } + // Check if we can dispel the master of the summon + else if(GetObjectSeen(oMaster) || GetObjectHeard(oMaster)) + { + // Dispel the caster + GlobalDispelTarget = oMaster; + GlobalDispelTargetHighestDispel = i5; + return; + } + // Else - normal dispel behaviour. I don't think targeting dispels at + // a summon will kill them! + } + // If not, we see if it is appropriate to even try and dispel them. + // - Stun, sleep, fear + if(AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalDispelTarget) || + // - Petrify + AI_GetAIHaveEffect(GlobalEffectPetrify, GlobalDispelTarget) || + // - Paralyze + AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalDispelTarget) || + // - Plot flag + GetPlotFlag(GlobalDispelTarget)) + { + if(GlobalDispelTarget == GlobalSpellTarget) + { + return; + } + else + { + // We can check GlobalSpellTarget if this target is not a good one + // to target. + GlobalDispelTarget = GlobalSpellTarget; + // Invalid + if(!GetIsObjectValid(GlobalDispelTarget) || + // - Stun, sleep, fear + AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalDispelTarget) || + // - Petrify + AI_GetAIHaveEffect(GlobalEffectPetrify, GlobalDispelTarget) || + // - Paralyze + AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalDispelTarget) || + // - Plot flag + GetPlotFlag(GlobalDispelTarget)) + { + return; + } + } + } + + // We check the spell it has been cast from :-) + int iSpellID, iSpellCounter; + // We check all of thier effects. + effect eCheck = GetFirstEffect(GlobalDispelTarget); + // Loop around valid effects. + while(GetIsEffectValid(eCheck) && + // Break loop if we are already at 5 + GlobalDispelTargetHighestDispel < i5 && + GlobalDispelTargetHighestBreach < i5) + { + iSpellID = GetEffectSpellId(eCheck); + // Make sure that it is equal, or over, 0 (IE acid fog) + // - Must be magical, else DispelMagic will not work. + if(iSpellID >= i0 && GetEffectSubType(eCheck) == SUBTYPE_MAGICAL) + { + // We then switch the spells (I tihnk it is easier to debug :-) ) + // and set what level of benifical enchantment it is :-) + switch(iSpellID) + { + // * 5 - Dispeled before hostile spells are cast at target + // Level 5 Breach Spells - should be Dispeled before attacking. + case SPELL_GREATER_SPELL_MANTLE: // Stops all offensive spells! + case SPELL_SPELL_MANTLE: // Stops all offensive spells! + case SPELL_LESSER_SPELL_MANTLE: // Stops all offensive spells! + case SPELL_SHADOW_SHIELD: // Immunity to negative energy, death spells + Necromancy! - so Dispel + case SPELL_ENERGY_BUFFER: // 40/- Elemental reistance can stop some level 9 spells! :-) + { + if(GlobalDispelTargetHighestDispel < i5) GlobalDispelTargetHighestDispel = i5; + if(GlobalDispelTargetHighestBreach < i5) GlobalDispelTargetHighestBreach = i5; + iSpellCounter++; + } + break; + // Level 5 other spells. + case SPELL_UNDEATHS_ETERNAL_FOE: // Stops quite a bit, and is a level 9 cleric spell. + case SPELL_HOLY_AURA: // 25 spell resistance! (and mind immunity) + case SPELL_UNHOLY_AURA:// 25 spell resistance! (and mind immunity) + case SPELL_SPELL_RESISTANCE: // 11 + Caster level in spell resistance. + case SPELL_DEATH_WARD: // Immunity to death spells - so Dispel + case SPELL_NEGATIVE_ENERGY_PROTECTION:// Immunity to negative energy, - so Dispel + case SPELL_TRUE_SEEING:// Can see anything - powerful against invis! + case SPELL_SHAPECHANGE:// VERY Powerful polymorphing. + case SPELL_PROTECTION_FROM_SPELLS:// +8 on all saves + { + if(GlobalDispelTargetHighestDispel < i5) GlobalDispelTargetHighestDispel = i5; + if(GlobalDispelTargetHighestBreach < i5) GlobalDispelTargetHighestBreach = i5; + iSpellCounter++; + } + break; + // * 4 - Dispeled just before level 7 or so spells. + case SPELL_PREMONITION: // Damage reduction 30/+5. Do this for fighters. + { + if(GlobalDispelTargetHighestDispel < i4) GlobalDispelTargetHighestDispel = i4; + if(GlobalDispelTargetHighestBreach < i4) GlobalDispelTargetHighestBreach = i4; + iSpellCounter++; + } + break; + // Most other decent protection spells, especially ones which will + // disrupt all castings. + case SPELL_CLARITY: // Immunity: mind spells. + case SPELL_MASS_HASTE: // Why should they be hasted! >:-D + case SPELL_HASTE: // Why should they be hasted! >:-D + case SPELL_PROTECTION_FROM_ELEMENTS: // 30/- Protection + case SPELL_REGENERATE: // +5 regen is quite powerful! + case SPELL_TENSERS_TRANSFORMATION://ANother powerful +AB ETC spell. + // High level summons + case SPELL_ELEMENTAL_SWARM: // Swarm + case SPELL_SUMMON_CREATURE_IX:// 9 + case SPELL_BLACK_BLADE_OF_DISASTER: // Black blade + case SPELL_SUMMON_CREATURE_VIII:// 8 + { + if(GlobalDispelTargetHighestDispel < i4) GlobalDispelTargetHighestDispel = i4; + iSpellCounter++; + } + break; + // * 3 - Dispeled just before level 4 or so spells + case SPELL_GLOBE_OF_INVULNERABILITY: // Stops level 1-4 spells. + case SPELL_GREATER_STONESKIN: // 20/+5 DR. Help fighters half-way though spells. + case SPELL_RESIST_ELEMENTS: // 20/- Protection + { + if(GlobalDispelTargetHighestDispel < i3) GlobalDispelTargetHighestDispel = i3; + if(GlobalDispelTargetHighestBreach < i3) GlobalDispelTargetHighestBreach = i3; + iSpellCounter++; + } + break; + // Increases in abilites, and some which stop level 4, or nearby, spells. + case SPELL_AURA_OF_VITALITY: // "All allies within the AOE gain +4 Str, Con, Dex"!! + case SPELL_FREEDOM_OF_MOVEMENT:// Freedom - web ETC may be affected (and slow!) + case SPELL_MAGIC_CIRCLE_AGAINST_EVIL:// +saves, AC, and mind immunity + case SPELL_MAGIC_CIRCLE_AGAINST_GOOD:// +saves, AC, and mind immunity + case SPELL_SEE_INVISIBILITY: // See through invisiblity + case SPELL_MESTILS_ACID_SHEATH: // 2/level + 1d6 acid damage - damage shield. + // Quite High level summons + case SPELL_SUMMON_CREATURE_VII: // 7 + case SPELL_SUMMON_CREATURE_VI: // 6 + case SPELL_SUMMON_CREATURE_V: // 5 + { + if(GlobalDispelTargetHighestDispel < i3) GlobalDispelTargetHighestDispel = i3; + iSpellCounter++; + } + break; + // * 2 - Dispeled just before level 2 or so spells. + case SPELL_MINOR_GLOBE_OF_INVULNERABILITY: // Immunity 1-2 level spells. + case SPELL_ETHEREAL_VISAGE: // Immunity 1-2 level spells (and some DR) + case SPELL_ENDURE_ELEMENTS: // 10/- Reduction. SPELL_GHOSTLY_VISAGE + case SPELL_GHOSTLY_VISAGE: // 0-1 level spell immunity, and 10% consealment + case SPELL_STONESKIN: // 10/+5 DR. Help fighters just before low-end spells. + { + if(GlobalDispelTargetHighestDispel < i2) GlobalDispelTargetHighestDispel = i2; + if(GlobalDispelTargetHighestBreach < i2) GlobalDispelTargetHighestBreach = i2; + iSpellCounter++; + } + break; + // Things that stop level 2 or 1 spells, and low-end ones which + // may hinder lower-end castings, and very powreful benifical + // enhancements. + case SPELL_DISPLACEMENT: // +50% consealment. Helps fighters. + case SPELL_EXPEDITIOUS_RETREAT:// +150% speed. Dispel here. + case SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE: // Immunity 1-2 level spells. + case SPELL_PROTECTION_FROM_EVIL:// Mind immunity mostly. + case SPELL_PROTECTION_FROM_GOOD:// Mind immunity mostly. + case SPELL_SHIELD: // +AC + Immunity magic missile + // Enhancements + case SPELL_DIVINE_POWER:// +Stat, HP, base attack bonus. + case SPELL_DIVINE_FAVOR:// +1-5 Attack/damage + case SPELL_ELEMENTAL_SHIELD: // +Damage Sheild (gives HTH attackers damage) + // Moderate level summons + case SPELL_SUMMON_CREATURE_IV: // 4 + case SPELL_SUMMON_CREATURE_III: // 3 + { + if(GlobalDispelTargetHighestDispel < i2) GlobalDispelTargetHighestDispel = i2; + iSpellCounter++; + } + break; + // * 1 - Lowest prioritory - Dispeled at the end. + // The rest - mostly low-end AC increases, bless and so forth. + // Before we attack, we Dispel them (and therefore help allies!) + case SPELL_MAGE_ARMOR: // +AC + { + if(GlobalDispelTargetHighestDispel < i1) GlobalDispelTargetHighestDispel = i1; + if(GlobalDispelTargetHighestBreach < i1) GlobalDispelTargetHighestBreach = i1; + iSpellCounter++; + } + // Non-breach + case SPELL_AID: // +Some HP and attack + case SPELL_AMPLIFY: // +20 listen + case SPELL_BARKSKIN: // +AC + case SPELL_BLESS: // +Attack, damage (AOE) + case SPELL_BLOOD_FRENZY:// +Attack, damage (Rage-like) + case SPELL_BULLS_STRENGTH:// +Stat + case SPELL_CATS_GRACE: // +Stat + case SPELL_EAGLE_SPLEDOR:// +Stat + case SPELL_ENTROPIC_SHIELD: // +20% consealment VS ranged. + case SPELL_OWLS_WISDOM: // +Stat + case AI_SPELL_OWLS_INSIGHT: // +Stat (Owls insight) + case SPELL_SANCTUARY: // Invisiblity-like. + case SPELL_SHIELD_OF_FAITH:// +2-5 AC + case SPELL_WAR_CRY: // +Attack, damage + case SPELL_WOUNDING_WHISPERS:// +Damage Sheild (gives HTH attackers damage) + case SPELL_STONE_BONES: // +3 AC to undead. + case SPELL_BATTLETIDE: // +2 Saves, attack, damage, also a negative AOE. + // Low level summons + case SPELL_SUMMON_CREATURE_II: // 2 + case SPELL_SUMMON_CREATURE_I: // 1 + { + if(GlobalDispelTargetHighestDispel < i1) GlobalDispelTargetHighestDispel = i1; + iSpellCounter++; + } + break; + } + } + eCheck = GetNextEffect(GlobalDispelTarget); + } + // We might dispel anything. + if(!GetSpawnInCondition(AI_FLAG_COMBAT_DISPEL_IN_ORDER, AI_COMBAT_MASTER)) + { + if(GlobalDispelTargetHighestBreach) GlobalDispelTargetHighestBreach = i5; + if(GlobalDispelTargetHighestDispel) GlobalDispelTargetHighestDispel = i5; + } + // Do we have a ton of spells? We add 1 to the prioritory for every 10 spells + // applied. + if(iSpellCounter > i5) GlobalDispelTargetHighestDispel += iSpellCounter / i10; + if(GlobalDispelTargetHighestDispel > i5) GlobalDispelTargetHighestDispel = i5; +} + +// Just sorts out sOriginalArrayName to sNewArrayName based on range only. +void AI_TargetingArrayDistanceStore(string sOriginalArrayName, string sNewArrayName) +{ + int bFilterPC; + if(sOriginalArrayName == ARRAY_TEMP_ENEMIES && + GetSpawnInCondition(AI_FLAG_TARGETING_FILTER_FOR_PC_TARGETS, AI_TARGETING_FLEE_MASTER)) + { + // Check for the nearest seen, enemy PC. + bFilterPC = GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN)); + } + // Make sure sNewArrayName is cleared + DeleteLocalInt(OBJECT_SELF, MAXINT_ + sNewArrayName); + int iCnt = i1; + float fSetUpRange; + // Now, we check for things like if to do melee or ranged, or whatever :-) + object oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + while(GetIsObjectValid(oTarget)) + { + if(!bFilterPC || GetIsPC(oTarget)) + { + fSetUpRange = GetDistanceToObject(oTarget); + // We set it to sNewArrayName - highest to lowest. + SetArrayFloatValue(sNewArrayName, oTarget, fSetUpRange); + } + // Next one + iCnt++; + oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + } + // If we have no valid targets, and PC's are on, we try again. + if(bFilterPC && !GetIsObjectValid(GetLocalObject(OBJECT_SELF, sNewArrayName + s1))) + { + // Re-run again. + iCnt = i1; + oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + while(GetIsObjectValid(oTarget)) + { + fSetUpRange = GetDistanceToObject(oTarget); + // We set it to sNewArrayName - highest to lowest. + SetArrayFloatValue(sNewArrayName, oTarget, fSetUpRange); + // Next one + iCnt++; + oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + } + } + // delete old ones. + int iCheck; + for(iCheck = i1; iCheck <= iCnt; iCheck++) + { + // Delete the old one + DeleteLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + } +} + +// Just sorts out sOriginalArrayName to sNewArrayName based on iType. +// iType 1 = AC, iType 2 = Total Saves, iType 3 = Phisical Protections, +// iType 4 = BAB, iType 5 = Hit Dice, iType 6 = Percent HP, iType 7 = Current HP, +// iType 8 = Maximum HP. 9 = Attacking us or not. +void AI_TargetingArrayIntegerStore(int iType, string sOriginalArrayName) +{ + int iCnt = i1; + int iValue; + // Now, we check for things like if to do melee or ranged, or whatever :-) + object oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + while(GetIsObjectValid(oTarget)) + { + if(iType == i1) + { + // AC + iValue = GetAC(oTarget); + } + else if(iType == i2) + { + // Total saving throws. + iValue = GetFortitudeSavingThrow(oTarget) + + GetReflexSavingThrow(oTarget) + + GetReflexSavingThrow(oTarget); + } + else if(iType == i3) + { + // Damage reduction + // 30/+5 + if(GetHasSpellEffect(SPELL_PREMONITION, oTarget)) + { + iValue = i30; + } + // 20/+5, 20/+3 + else if(GetHasSpellEffect(SPELL_GREATER_STONESKIN, oTarget) || + GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, oTarget)) + { + iValue = i20; + } + // 10/+5 10/+3 + else if(GetHasSpellEffect(SPELL_SHADOW_SHIELD, oTarget) || + GetHasSpellEffect(SPELL_STONESKIN, oTarget)) + { + iValue = i10; + } + // 5/+1 + else if(GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, oTarget)) + { + iValue = i5; + } + else + { + iValue = i0; + } + } + else if(iType == i4) + { + // BAB + iValue = GetBaseAttackBonus(oTarget); + } + else if(iType == i5) + { + // Hit dice + iValue = GetHitDice(oTarget); + } + else if(iType == i6) + { + // %HP + iValue = AI_GetPercentOf(GetCurrentHitPoints(oTarget), GetMaxHitPoints(oTarget)); + } + else if(iType == i7) + { + // Current + iValue = GetCurrentHitPoints(oTarget); + } + else if(iType == i8) + { + // Max + iValue = GetMaxHitPoints(oTarget); + } + else if(iType == i9) + { + // Sneak attack. + iValue = FALSE; + if(GetAttackTarget(oTarget) != OBJECT_SELF && + !GetIsImmune(oTarget, IMMUNITY_TYPE_SNEAK_ATTACK)) + { + iValue = TRUE; + } + } + // We set it to the new array name - highest to lowest. + SetArrayIntegerValue(ARRAY_TEMP_ARRAY, oTarget, iValue); + // Delete the old one + if(iType != i9)// Sneak attack, if not a valid sneak target, we target normally so don't delete + { + DeleteLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + } + // Next target + iCnt++; + oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt)); + } +} +// Deletes all FLoats, Integers and Objects set to sArray for valid +// objects got by GetLocalObject to sArray. +void AI_TargetingArrayDelete(string sArray) +{ + int iCnt = i1; + string sCnt = IntToString(iCnt); + object oLocal = GetLocalObject(OBJECT_SELF, sArray + sCnt); + while(GetIsObjectValid(oLocal)) + { + // Delete all + DeleteLocalFloat(OBJECT_SELF, sArray + sCnt); + DeleteLocalInt(OBJECT_SELF, sArray + sCnt); + DeleteLocalObject(OBJECT_SELF, sArray + sCnt); + // Get next object + iCnt++; + sCnt = IntToString(iCnt); + oLocal = GetLocalObject(OBJECT_SELF, sArray + sCnt); + } + DeleteLocalInt(OBJECT_SELF, MAXINT_ + sArray); +} + +// This sets ARRAY_TEMP_ARRAY of integer values to sNewArrayName. +// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER. +// - We work until iMinimum is filled, or we get to iMinimum and we get to +// a target with value > iImputMinimum. (20 - 25 > X?) +int AI_TargetingArrayLimitTargets(string sNewArrayName, int iTypeOfTarget, int iImputMinLimit, int iMinLoop, int iMaxLoop) +{ + int iAddEachTime, iValue, iCnt, iCnt2, iCnt3, iBreak, iLowestHighestValue; + string sCnt; + object oSetUpTarget; + // Is it lowest to highest, or highest to lowest? + // - 1. Lowest to highest + if(iTypeOfTarget == TARGET_LOWER) + { + iAddEachTime = i1; + iCnt = i1; + } + else // if(iTypeOfTarget == TARGET_HIGHER) + { + // Change to start at top value, and work down. + iAddEachTime = iM1; + iCnt = GetLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY); + } + + // Start loop from array based on AC. Overrides exsisting targets + // - Use temp enemy array. ARRAY_TEMP_ARRAY + iCnt2 = i0; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE) + { + // We check AC or whatever... + iValue = GetLocalInt(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + // Delete objects + DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + DeleteLocalInt(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + // Check values. + // we check if we have under minimum OR it is under/over X. + // As it is in value order, once we get to a point to stop, we + // break the loop. + if((iCnt2 < iMaxLoop) && + ((iCnt2 < iMinLoop) /*Default, get minimum targets*/ + || (iValue - iLowestHighestValue < iImputMinLimit && iTypeOfTarget == TARGET_LOWER)// Lowest to highest (20 - 25 < X?) + || (iLowestHighestValue - iValue < iImputMinLimit && iTypeOfTarget == TARGET_HIGHER)))// Highest to lowest (25 - 20 < X?) + { + // Set this as the newest highest/lowest value + iLowestHighestValue = iValue; + // Add it to array. + iCnt2++; + sCnt = IntToString(iCnt2); + SetLocalObject(OBJECT_SELF, sNewArrayName + sCnt, oSetUpTarget); + SetLocalInt(OBJECT_SELF, sNewArrayName + sCnt, iValue); + } + else // else break out. (If got to max targets, got to a target we don't want to target) + { + for(iCnt3 = iCnt; iBreak != TRUE; iCnt3 += iAddEachTime) + { + // Remove all other values in the loop. + sCnt = IntToString(iCnt3); + if(GetIsObjectValid(GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt))) + { + DeleteLocalInt(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + } + else + { + iBreak = TRUE; + } + } + iBreak = TRUE; + } + // Get next AC target - we add X, which is either +1 or -1, to + // continue a loop going up or down. + iCnt += iAddEachTime; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + } + // Start resetting temp array used in the rest. + DeleteLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY); + // Returns the amount of targets stored. + return iCnt2; +} +// This sets ARRAY_TEMP_ARRAY of float values to sNewArrayName. +// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER. +// - We work until iMinimum is filled, or we get to iMinimum and we get to +// a target with value > iImputMinimum. (20.0 - 25.0 > X?) +// Returns the amount of targets set in sNewArrayName. +int AI_TargetingArrayLimitTargetsFloat(string sNewArrayName, int iTypeOfTarget, float fImputMinLimit, int iMinLoop, int iMaxLoop) +{ + int iAddEachTime, iCnt, iCnt2, iCnt3, iBreak; + float fValue, fLowestHighestValue; + string sCnt; + object oSetUpTarget; + // Is it lowest to highest, or highest to lowest? + // - 1. Lowest to highest + if(iTypeOfTarget == TARGET_LOWER) + { + iAddEachTime = i1; + iCnt = i1; + } + else // if(iTypeOfTarget == TARGET_HIGHER) + { + // Change to start at top value, and work down. + iAddEachTime = iM1; + iCnt = GetLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY); + } + + // Start loop from array based on AC. Overrides exsisting targets + // - Use temp enemy (AC) array. + iCnt2 = FALSE;// Reset counter + iCnt = i1; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE) + { + // We check range normally... + fValue = GetLocalFloat(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + // Check values. + // we check if we have under minimum OR it is under/over X. + // As it is in value order, once we get to a point to stop, we + // break the loop. + if((iCnt2 < iMaxLoop) && + ((iCnt2 < iMinLoop) /*Default, get minimum targets*/ + || (fValue - fLowestHighestValue < fImputMinLimit && iTypeOfTarget == TARGET_LOWER)// Lowest to highest (20 - 25 < X?) + || (fLowestHighestValue - fValue < fImputMinLimit && iTypeOfTarget == TARGET_HIGHER)))// Highest to lowest (25 - 20 < X?) + { + // Set fLowestHighestValue + fLowestHighestValue = fValue; + // Add it to array. + iCnt2++; + sCnt = IntToString(iCnt2); + SetLocalObject(OBJECT_SELF, sNewArrayName + sCnt, oSetUpTarget); + SetLocalFloat(OBJECT_SELF, sNewArrayName + sCnt, fValue); + } + else // else break out. (If got to max targets, got to a target we don't want to target) + { + for(iCnt3 = iCnt; iBreak != TRUE; iCnt3 += iAddEachTime) + { + // Remove all other values in the loop. + sCnt = IntToString(iCnt3); + if(GetIsObjectValid(GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt))) + { + DeleteLocalFloat(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + } + else + { + iBreak = TRUE; + } + } + iBreak = TRUE; + } + // Delete objects + DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + DeleteLocalFloat(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + // Get next AC target - we add X, which is either +1 or -1, to + // continue a loop going up or down. + iCnt += iAddEachTime; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt); + } + // Start resetting temp array used in the rest. + DeleteLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY); + // Returns the amount of targets stored. + return iCnt2; +} + +// Makes sure oTarget isn't: +// - Dead +// - Petrified +// - AI Ignore ON +// - DM +// Must be: Seen or heard +// Returns: TRUE if any of these are true. +int AI_GetTargetSanityCheck(object oTarget) +{ + if(!GetIsObjectValid(oTarget) || // Isn't valid + GetIsDead(oTarget) || // Is dead + GetIsDM(oTarget) || // Is DM + GetIgnore(oTarget) || // Is ignored + AI_GetAIHaveEffect(GlobalEffectPetrify, oTarget) || // Is petrified + (!GetObjectSeen(oTarget) && !GetObjectHeard(oTarget))) // Is not seen nor heard. + { + return TRUE; + } + return FALSE; +} + +// We set up targets to Global* variables, GlobalSpellTarget, GlobalRangeTarget, +// and GlobalMeleeTarget. Counts enemies, and so on. +// - Uses oIntruder (to attack or move near) if anything. +// - We return TRUE if it ActionAttack's, or moves to an enemy - basically +// that we cannot do an action, but shouldn't search. False if normal. +int AI_SetUpAllObjects(object oImputBackup) +{ + // Delete past arrays + AI_TargetingArrayDelete(ARRAY_ENEMY_RANGE); + AI_TargetingArrayDelete(ARRAY_ENEMY_RANGE_SEEN); + AI_TargetingArrayDelete(ARRAY_ENEMY_RANGE_HEARD); + AI_TargetingArrayDelete(ARRAY_ALLIES_RANGE); + AI_TargetingArrayDelete(ARRAY_ALLIES_RANGE_SEEN); + AI_TargetingArrayDelete(ARRAY_ALLIES_RANGE_SEEN_BUFF); + + // We always use range as at least 1 of the ways to get best target. + float fCurrentRange, fSetMaxWeCanGoTo; + location lSelf = GetLocation(OBJECT_SELF); + int iCnt, iCnt2, iCnt3, iBreak;//Counter loop. + int iValue, iMaxTurnsAttackingX, iMaximum, iMinimum, iRemainingTargets, iTypeOfTarget; + string sCnt; // IntToString(iCnt) :-) + object oTempLoopObject, oSetUpTarget, oLastHostile, oLastTarget, oImputBackUpToAttack; + + // Note: Here we check if oIntruder is a sane thing to attack. + if(GetIsObjectValid(oImputBackup) && !GetIgnoreNoFriend(oImputBackup)) + { + oImputBackUpToAttack = oImputBackup; + } + // Note: oIntruder is still used to search near if anything. + + // SRA - Spell Ranged Attacking uses the nearest seen, or the nearest heard, + // for the spell target (not ranged or melee, however) + if(SRA) + { + // Checked below for validness ETC. + GlobalSpellTarget = GlobalNearestEnemySeen; + } + + // We set up 2 other targets...for testing against ETC. + oLastHostile = GetLastHostileActor(); + iMaxTurnsAttackingX = GetBoundriedAIInteger(AI_MAX_TURNS_TO_ATTACK_ONE_TARGET, i6, i40, i1); + + // Start... + // Gets all CREATURES within 50M andin LOS. THIS IS THE MAJOR OBJECT LOOP OF THE AI! + GlobalTotalPeople = FALSE;// Reset + oSetUpTarget = GetFirstObjectInShape(SHAPE_SPHERE, f50, lSelf, TRUE); + while(GetIsObjectValid(oSetUpTarget)) + { + // We totally ignore DM's, and AI_IGNORE people + // - We don't use us as a target + // - We do dead people later, special with GetNearestCreature + if(!GetIgnore(oSetUpTarget) && !GetIsDM(oSetUpTarget) && + !GetIsDead(oSetUpTarget) && oSetUpTarget != OBJECT_SELF) + { + // We count +1 more person in our LOS + GlobalTotalPeople++; + // If the target is a friend, we add 1 to targets, and set in array. + if(GetIsFriend(oSetUpTarget) || GetFactionEqual(oSetUpTarget)) + { + GlobalTotalAllies++; + SetLocalObject(OBJECT_SELF, ARRAY_TEMP_ALLIES + IntToString(GlobalTotalAllies), oSetUpTarget); + } + // Enemy...add to enemy array, even if not seen nor heard. + else if(GetIsEnemy(oSetUpTarget)) + { + //SpeakString("Enemy (no dead) in LOS:" + GetName(oSetUpTarget)); + // We set up a "Seen or heard" enemies counter below + iCnt++; + SetLocalObject(OBJECT_SELF, ARRAY_TEMP_ENEMIES + IntToString(iCnt), oSetUpTarget); + } + } + oSetUpTarget = GetNextObjectInShape(SHAPE_SPHERE, f50, lSelf, TRUE); + } + // The first simple one is therefore done :-) + +/*::////////////////////////////////////////////// + Special case: + + If we have NO nearest seen/heard enemies (GetNearest* calls) we: + - Check allies for thier targets + + Nothing else for now. +//::////////////////////////////////////////////*/ + + +/*::////////////////////////////////////////////// + Enemies + + Set up finalish arrays of enemies, and lots of counting numbers. +//::////////////////////////////////////////////*/ + + // Next, we loop on range. Allies and Enemies + AI_TargetingArrayDistanceStore(ARRAY_TEMP_ENEMIES, ARRAY_ENEMY_RANGE); + + // Before we start re-setting targets, we do set up an extra 2 arrays based + // on seen and heard (one OR the other) arrays for the enemy. These are objects in our LOS. + iCnt = i1; + iCnt2 = FALSE;// Counter for seen + iCnt3 = FALSE;// Counter for heard + iValue = FALSE;// Counter for BAB + iBreak = FALSE;// Counter for HD + GlobalEnemiesIn4Meters = FALSE;// Make sure at 0 + GlobalMeleeAttackers = FALSE;// Make sure at 0 + GlobalRangedAttackers = FALSE;// Make sure at 0 + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget)) + { + fCurrentRange = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE + IntToString(iCnt)); + // Well, we have enemies in range. We check if we need to re-set constants + // based on a timer. If done, set a timer. + if(!GetLocalInt(oSetUpTarget, AI_JASPERRES_EFFECT_SET) && + !GetLocalInt(oSetUpTarget, AI_TIMER_EFFECT_SET)) + { + AI_SetEffectsOnTarget(oSetUpTarget); + SetLocalInt(oSetUpTarget, AI_TIMER_EFFECT_SET, TRUE); + // Just set effects every round, not every possible action (2 with haste) + DelayCommand(f5, DeleteLocalInt(oSetUpTarget, AI_TIMER_EFFECT_SET)); + } + // Don't target things who are petrified. + if(!AI_GetAIHaveEffect(GlobalEffectPetrify, oSetUpTarget) && !GetIsDead(oSetUpTarget)) + { + // Count ranged and melee attackers. + if(GetAttackTarget(oSetUpTarget) == OBJECT_SELF) + { + // Melee/ranged attacker? + if(GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSetUpTarget))) + { + // - 1.3 beta fixed this. I didn't notice I got these mixed up :-P + GlobalRangedAttackers++; + } + else + { + GlobalMeleeAttackers++; + } + } + // Total enemies in 4M + if(fCurrentRange <= f4) + { + GlobalEnemiesIn4Meters++; + } + // It is nearest to futhest. Just set each one in one of 2 arrays. + if(GetObjectSeen(oSetUpTarget)) + { + // iBreak counts average HD + iBreak += GetHitDice(oSetUpTarget); + // Value counts BAB + iValue += GetBaseAttackBonus(oSetUpTarget); + // Add to total seen/heard enemies + GlobalTotalSeenHeardEnemies++; + // Object seen. + iCnt2++; + SetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt2), fCurrentRange); + SetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt2), oSetUpTarget); + } + else if(GetObjectHeard(oSetUpTarget)) + { + // iBreak counts average HD + iBreak += GetHitDice(oSetUpTarget); + // Value counts BAB + iValue += GetBaseAttackBonus(oSetUpTarget); + // Add to total seen/heard enemies + GlobalTotalSeenHeardEnemies++; + // Object heard. + iCnt3++; + SetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt3), fCurrentRange); + SetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt3), oSetUpTarget); + } + } + // Next enemy + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE + IntToString(iCnt)); + } + // set GlobalRangeToFuthestEnemy. + // - Using futhest heard should be good enough. + GlobalRangeToFuthestEnemy = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt3)); + + // We need value just above to calcualte these, else leave at 0. + if(GlobalTotalSeenHeardEnemies >= i1 && iBreak >= i1) + { + // Average enemy HD + GlobalAverageEnemyHD = iBreak / GlobalTotalSeenHeardEnemies; + if(GlobalAverageEnemyHD < i1) GlobalAverageEnemyHD = i1; + // Average BAB + GlobalAverageEnemyBAB = iValue / GlobalTotalSeenHeardEnemies; + if(GlobalAverageEnemyBAB < i0) GlobalAverageEnemyBAB = i0; + } + +/*::////////////////////////////////////////////// + Friends + + Sets up all allies things +//::////////////////////////////////////////////*/ + + // Friendly (ally) targets too - for curing ETC. + AI_TargetingArrayDistanceStore(ARRAY_TEMP_ALLIES, ARRAY_ALLIES_RANGE); + + // Spells which affect GetIsReactionType + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + s1); + + // If not valid, use self + if(!GetIsObjectValid(oSetUpTarget)) + { + // Use nearest us, just for testing. + GlobalFriendlyFireHostile = GetIsReactionTypeHostile(OBJECT_SELF); + GlobalFriendlyFireFriendly = GetIsReactionTypeFriendly(OBJECT_SELF); + } + + // Use nearest ally, just for testing. + GlobalFriendlyFireHostile = GetIsReactionTypeHostile(oSetUpTarget); + GlobalFriendlyFireFriendly = GetIsReactionTypeFriendly(oSetUpTarget); + GlobalNearestAlly = oSetUpTarget; + GlobalValidAlly = GetIsObjectValid(GlobalNearestAlly); + GlobalRangeToAlly = GetDistanceToObject(oSetUpTarget); + + // 0 Seen/heard. We don't check enemies numbers - they could be hidden. + if(!GlobalValidNearestSeenEnemy && !GlobalValidNearestHeardEnemy) + { + iBreak = FALSE; + // Loop allies for ANY target, and move to them! (Attack in HTH, this + // lets us re-set targets On Percieve) + iCnt = iM1; + // Allys + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE) + { + // It is nearest to futhest. + oTempLoopObject = GetAttackTarget(oSetUpTarget); + if(GetIsObjectValid(oTempLoopObject)) + { + // If a valid attack object, we stop! + iBreak = TRUE; + } + else + { + // Next ally + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt)); + } + } + // We move to the target if we have one. + if(iBreak) + { + // Just a temp most damaging melee. Normally, it is best to + // just Move to the target + // 39: "[DCR:Targeting] No valid enemies in sight, moving to allies target's. [Target] " + GetName(oSetUpTarget) + DebugActionSpeakByInt(39, oSetUpTarget); + AI_EquipBestShield(); + ActionEquipMostDamagingMelee(); + ActionMoveToLocation(GetLocation(oSetUpTarget), TRUE); + return TRUE; + } + } + // What other ally stuff? + // - Nearest leader + // - Who to heal? (Most damaged in X range of lesser damaged ones) + // - Who to heal effects from? (Not done here!) + // - Nearest ally + // - Buff ally + if(GlobalValidAlly) + { + // Set up an array of seen ranged-based allies, for healing and for + // healing effects, and for buffing. + iCnt = i1; + iCnt2 = i0; + iCnt3 = i0; + iValue = FALSE; // Just a temp for leader status set + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget)) + { + iCnt3 += GetHitDice(oSetUpTarget); + if(GetObjectSeen(oSetUpTarget)) + { + iCnt2++; + SetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt2), oSetUpTarget); + // Set global nearest seen leader for morale ETC. + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oSetUpTarget) + && iValue != TRUE) + { + GlobalNearestLeader = oSetUpTarget; + iValue = TRUE; + } + } + // Next ally + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + } + GlobalValidLeader = iValue; + if(iCnt >= i1 && iCnt3 >= i1) + { + GlobalAverageFriendlyHD = iCnt3 / iCnt; + } + // Set nearest seen ally. + GlobalNearestSeenAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + s1); + GlobalValidSeenAlly = GetIsObjectValid(GlobalNearestSeenAlly); + + // We set up buff allies in a new array. + // - We may set up summons (or people with masters) if we have under + // <= 3 people, or it is of comparable hit dice to us. + iCnt = i1; + iCnt2 = FALSE; + fSetMaxWeCanGoTo = f20; + // we add this onto any ranges we input, so how far we'll move to. + GlobalBuffRangeAddon = 0.0; + // iValue is the limit of buff targets + // - Less if sorceror/bard (very few) + // - More if proper buffer + iValue = i5; + if(GlobalWeAreSorcerorBard) + { + iValue = i2; + } + // If we are set to buff allies, we extend the range. + if(GetSpawnInCondition(AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS, AI_COMBAT_MASTER)) + { + iValue = i8; + fSetMaxWeCanGoTo = f40; + GlobalBuffRangeAddon = f30; + } + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + // Loop allies + // - Only up to 10. + // - Check masters as above. + while(GetIsObjectValid(oSetUpTarget) && iCnt2 <= iValue && + GetDistanceToObject(oSetUpTarget) <= fSetMaxWeCanGoTo) + { + // No arcane spellcasters. + if(!GetLevelByClass(CLASS_TYPE_SORCERER, oSetUpTarget) && + !GetLevelByClass(CLASS_TYPE_WIZARD, oSetUpTarget) && + // - Master check + (GlobalTotalAllies <= i3 || + !GetIsObjectValid(GetMaster(oSetUpTarget)) || + GetHitDice(oSetUpTarget) >= GlobalOurHitDice - i5)) + { + // Add to new array + iCnt2++; + SetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + IntToString(iCnt2), oSetUpTarget); + } + // Next seen ally + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); + } + } + +/*::////////////////////////////////////////////// + Well, do we change our targets? + + There is a max limit (default of 6) rounds that we attack targets, melee + ranged and spell. + + There is also a random % to re-set the target, for each type. + + Leaders override all 3 targets, and set it to it. + + And finally, the target we had last time must be: + - Not dead + - Seen or heard (Spell targets must be seen if there is a valid seen enemy) + - Not attacking us if we have sneak attack (Ranged/Melee target) +//::////////////////////////////////////////////*/ + + // Leader checking + oSetUpTarget = GetAIObject(AI_ATTACK_SPECIFIC_OBJECT); + // Always delete + DeleteAIObject(AI_ATTACK_SPECIFIC_OBJECT); + // Our specific target to attack + // - sanity check just in case + if(!AI_GetTargetSanityCheck(oSetUpTarget)) + { + // 40: "[DCR:Targeting] Override Target Seen. [Name]" + GetName(oSetUpTarget) + DebugActionSpeakByInt(40, oSetUpTarget); + // Melee target, we must check if we have any valid. + GlobalMeleeTarget = oSetUpTarget; + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + } + // Lagbusting, get nearest + else if(GetSpawnInCondition(AI_FLAG_OTHER_LAG_TARGET_NEAREST_ENEMY, AI_TARGETING_FLEE_MASTER)) + { + if(GetIsObjectValid(GlobalNearestEnemySeen)) + { + // The validness of these are checked later anyway + GlobalMeleeTarget = GlobalNearestEnemySeen; + GlobalRangedTarget = GlobalNearestEnemySeen; + GlobalSpellTarget = GlobalNearestEnemySeen; + } + } + // We may also make it so that we target only the lowest AC and so on... + // LIKE_LOWER_HP, LIKE_LOWER_AC, LIKE_MAGE_CLASSES, LIKE_ARCHERS + else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_HP, AI_TARGETING_FLEE_MASTER)) + { + // We use this check - inbuilt, automatic, and also is simple! + oSetUpTarget = GetFactionMostDamagedMember(GlobalNearestEnemySeen); + if(GetIsObjectValid(oSetUpTarget)) + { + if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 || + GetDistanceToObject(oSetUpTarget) < f3) + { + GlobalMeleeTarget = oSetUpTarget; + } + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + } + } + // HD + else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_HD, AI_TARGETING_FLEE_MASTER)) + { + // We use this check - inbuilt, automatic, and also is simple! + oSetUpTarget = GetFactionWeakestMember(GlobalNearestEnemySeen); + if(GetIsObjectValid(oSetUpTarget)) + { + if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 || + GetDistanceToObject(oSetUpTarget) < f3) + { + GlobalMeleeTarget = oSetUpTarget; + } + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + } + } + // AC + else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_AC, AI_TARGETING_FLEE_MASTER)) + { + // We use this check - inbuilt, automatic, and also is simple! + oSetUpTarget = GetFactionWorstAC(GlobalNearestEnemySeen); + if(GetIsObjectValid(oSetUpTarget)) + { + // Make sure the nearest seen is not in front of the worst AC. + if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 || + GetDistanceToObject(oSetUpTarget) < f3) + { + GlobalMeleeTarget = oSetUpTarget; + } + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + } + } + // Ranged attackers + else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_ARCHERS, AI_TARGETING_FLEE_MASTER)) + { + // Get nearest one who is attacking us. + iCnt = i1; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget)) + { + // have they got a ranged weapon? + if(GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSetUpTarget))) + { + if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 || + GetDistanceToObject(oSetUpTarget) < f3) + { + GlobalMeleeTarget = oSetUpTarget; + } + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + break; + } + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + } + } + // Mage classes + else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_MAGE_CLASSES, AI_TARGETING_FLEE_MASTER)) + { + // Sorceror + oSetUpTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_SORCERER, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oSetUpTarget) || + (!GetObjectSeen(oSetUpTarget) && !GetObjectHeard(oSetUpTarget))) + { + // Wizard + oSetUpTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_WIZARD, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_IS_ALIVE, TRUE); + } + // If valid, use + if(GetIsObjectValid(oSetUpTarget) && + (GetObjectSeen(oSetUpTarget) || GetObjectHeard(oSetUpTarget))) + { + if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 || + GetDistanceToObject(oSetUpTarget) < f3) + { + GlobalMeleeTarget = oSetUpTarget; + } + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + } + } + iValue = GetAIConstant(AI_FAVOURED_ENEMY_RACE); + if(iValue >= FALSE) + { + oSetUpTarget = GetNearestCreature(CREATURE_TYPE_RACIAL_TYPE, iValue, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + if(GetIsObjectValid(oSetUpTarget)) + { + // Make sure the nearest seen is not in front of the worst AC. + if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 || + GetDistanceToObject(oSetUpTarget) < f3) + { + GlobalMeleeTarget = oSetUpTarget; + } + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + } + } + else + { + iValue = GetAIConstant(AI_FAVOURED_ENEMY_CLASS); + if(iValue >= FALSE) + { + oSetUpTarget = GetNearestCreature(CREATURE_TYPE_CLASS, iValue, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); + if(GetIsObjectValid(oSetUpTarget)) + { + // Make sure the nearest seen is not in front of the worst AC. + if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 || + GetDistanceToObject(oSetUpTarget) < f3) + { + GlobalMeleeTarget = oSetUpTarget; + } + GlobalRangedTarget = oSetUpTarget; + GlobalSpellTarget = oSetUpTarget; + } + } + } + +/*::////////////////////////////////////////////// + Melee targeting + + This uses some of the closer people or all within reach (determined before) + + After getting the right people, we determine with AC, HP and some other + things, which are the best. +//::////////////////////////////////////////////*/ + + // Do we want to change melee targets? + oLastTarget = GetAIObject(AI_LAST_MELEE_TARGET); + + // iValid is the counter for attacking target X... + iBreak = GetAIInteger(AI_MELEE_TURNS_ATTACKING); + iBreak++; + SetAIInteger(AI_MELEE_TURNS_ATTACKING, iBreak); + // We set this to 0 if we our oSetUpTarget != GlobalMeleeTarget below changing. + + // Check % + // If we have an override, we don't check for targets, but run Global* + // setups at the end still, but with the target being oOverride. + + // - No valid override target + if(!GetIsObjectValid(GlobalMeleeTarget) && + (AI_GetTargetSanityCheck(oLastTarget) || // Sanity check (dead, valid, ETC) + // And must pass a % roll + d100() <= GetBoundriedAIInteger(AI_MELEE_LAST_TO_NEW_TARGET_CHANCE, i20, i100) || + // OR last target attacked for X rounds + iBreak >= iMaxTurnsAttackingX)) + { + // Loop targets, ETC, and get GlobalMeleeTarget normally. + // Set up melee = ARRAY_MELEE_ENEMY + + // Note: if we start getting wide gaps between group A and object B, we + // stop, because they are probably behind more enemies :-D + // Note 2: We only use seen enemies as a prioritory, else heard ones (like + // invisible ones) + + // 1. Nearby SEEN enemies. Obviously nearest to furthest! + // IE the maximum and minimum targets for the range checking. + iMinimum = GetBoundriedAIInteger(TARGETING_RANGE + MINIMUM, i2, i40, i1); + iMaximum = GetBoundriedAIInteger(TARGETING_RANGE + MAXIMUM, i8, i40, i1); + + // fSetMaxWeCanGoTo = Maximum range away from us (GetDistanceToObject) that + // we can go to. This is increased if we have tumble (compared to level) or + // spring attack is king :-D + fSetMaxWeCanGoTo = GlobalOurReach + f1;// Have 1 extra as well + // We add a lot for spring attack - 3d4 (3 to 12) + // OR we have 13+ (IE a very high chance of suceeding) in tumble + if(GetHasFeat(FEAT_SPRING_ATTACK) || GetSkillRank(SKILL_TUMBLE) >= i13) + { + fSetMaxWeCanGoTo += IntToFloat(d4(i3)); + } + else if(GetHasSkill(SKILL_TUMBLE)) + { + // Else we add some for tumble + iBreak = GetSkillRank(SKILL_TUMBLE) - GlobalOurHitDice; + if(iBreak > FALSE) + { + // * Basis of Skill Rank - Our Hit Dice. 5 tumble on a level 2 makes +3M Range. + fSetMaxWeCanGoTo += IntToFloat(iBreak); + } + } + iBreak = FALSE; + // Start loop from array based on range. + // - Use seen array! + // - Break if we have added enough targets to our array. + iRemainingTargets = FALSE; + iCnt = i1; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt); + while(GetIsObjectValid(oSetUpTarget) && iRemainingTargets < iMaximum) + { + // If seen, we check range... + fCurrentRange = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt); + // We start from the closest, so we need to cancle those out which + // are too far away. + // we check if it is in our reach. If so, we add it. + if(fCurrentRange < fSetMaxWeCanGoTo || iRemainingTargets < iMinimum) + { + iRemainingTargets++; + sCnt = IntToString(iRemainingTargets); + SetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, oSetUpTarget); + SetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, fCurrentRange); + } + // Get next nearest SEEN + iCnt++; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt); + } + // 1a. If not valid, just use nearest seen if valid + if(!iRemainingTargets && GlobalValidNearestSeenEnemy) + { + iRemainingTargets = i1; + SetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1, GlobalNearestEnemySeen); + SetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1, GetDistanceToObject(GlobalNearestEnemySeen)); + } + else + { + // 2. Make sure the nearest HEARD is NOT very close compared to the nearest + // SEEN OR we have no nearest seen. + // Check if we have no seen objects set, or the nearest heard is nearer then the futhest seen by 4M + fCurrentRange = GetDistanceToObject(GlobalNearestEnemyHeard); + if(GlobalValidNearestHeardEnemy && (!iRemainingTargets || + // Range to nearest heard is nearer then the melee enemy + (fCurrentRange > f0 && ((fCurrentRange + f4) < + // Nearest melee range enemy we've set to seen + GetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1))))) + { + // - We half the amount we need for this range check + // Maximum + iMaximum /= i2; + if(iMaximum < i1) iMaximum = i1; + // Minimum + iMinimum /= i2; + if(iMinimum < i1) iMinimum = i1; + // Start loop from array based on range. Overrides exsisting targets + // - Use heard enemy array. + iCnt = i1; + iRemainingTargets = FALSE; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + sCnt); + while(GetIsObjectValid(oSetUpTarget) && iRemainingTargets < iMaximum) + { + // If seen, we check range... + fCurrentRange = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + sCnt); + // We start from the closest, so we need to cancle those out which + // are too far away. + if(fCurrentRange < fSetMaxWeCanGoTo || iRemainingTargets < iMinimum) + { + iRemainingTargets++; + sCnt = IntToString(iRemainingTargets); + SetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, oSetUpTarget); + SetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, fCurrentRange); + } + // Get next HEARD + iCnt++; + sCnt = IntToString(iCnt); + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + sCnt); + } + } + } + // Now, do we have any Melee Targets? + if(!iRemainingTargets) + { + // If not, what can we use? + // - We use the imputted target! + // - We use anyone who allies are attacking! + // - We use anyone we hear! + + // Imputted target (EG last attack that GetObjectSeen doesn't return + // for). + oSetUpTarget = oImputBackUpToAttack; + if(!GetIsObjectValid(oSetUpTarget)) + { + // Anyone we hear + oSetUpTarget = GlobalNearestEnemyHeard; + if(!GetIsObjectValid(oSetUpTarget)) + { + // We get who our allies are attacking + iCnt = i1; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE) + { + // Get the allies attack target. If valid, attack it! + oTempLoopObject = GetAttackTarget(oSetUpTarget); + if(GetIsObjectValid(oTempLoopObject) && + !GetFactionEqual(oTempLoopObject) && + !GetIsFriend(oTempLoopObject)) + { + oSetUpTarget = oTempLoopObject; + iBreak = TRUE; + } + else + { + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt)); + } + } + // Don't attack anyone we got in the loop, of course. + if(GetIsFriend(oSetUpTarget) || GetFactionEqual(oSetUpTarget)) + { + oSetUpTarget = OBJECT_INVALID; + } + } + } + // Do we have a target from those backups above? + // - If so, we ActionAttack it so we move near it, as it is not in + // our LOS, or isn't in our seen range in our LOS. + if(GetIsObjectValid(oSetUpTarget)) + { + // 41: [DCR:Targeting] No seen in LOS, Attempting to MOVE to something [Target]" + GetName(oSetUpTarget) + DebugActionSpeakByInt(41, oSetUpTarget); + DeleteAIObject(AI_LAST_MELEE_TARGET); + DeleteAIObject(AI_LAST_SPELL_TARGET); + DeleteAIObject(AI_LAST_RANGED_TARGET); + AI_EquipBestShield(); // in case of an ambush, be ready + ActionMoveToLocation(GetLocation(oSetUpTarget), TRUE); + return TRUE; + } + // If it doesn't have anything we can see/move to/attack/search + // near, then we make sure have make sure we have no target + // (GlobalAnyValidTargetObject) and break the function, so we can stop. + // "else" + GlobalAnyValidTargetObject = FALSE; + return FALSE; + } + // If only one, choose it + else if(iRemainingTargets == i1) + { + GlobalMeleeTarget = GetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1); + } + else //if(iRemainingTargets > i1) + { + // If we have any set targets (heard/seen ones) then we + // will reduce the amount by AC, HP (current Percent/max/current) + // and HD and BAB + + // We only use range as a default. The rest are done in order if they + // are set! :-D + + // We check for PC targets + // DODODODO + + // Target: + // AC - Used only for phisical attacks (TARGETING_AC) + // Phisical Protections - Used for both spells and melee (TARGETING_PHISICALS) + // Base Attack Bonus - Used for both spells and melee (TARGETING_BAB) + // Hit Dice - Used for both spells and melee (TARGETING_HITDICE) + // HP Percent - Used for both spells and melee (TARGETING_HP_PERCENT) + // HP Current - Used for both spells and melee (TARGETING_HP_CURRENT) + // HP Maximum - Used for both spells and melee (TARGETING_HP_MAXIMUM) + + // 0. Sneak attack check + if(GetHasFeat(FEAT_SNEAK_ATTACK)) + { + // We normally get the nearest enemy who is not attacking us, and + // actually stop! + AI_TargetingArrayIntegerStore(i9, ARRAY_MELEE_ENEMY); + + // Get the closest who isn't attacking us. + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + s1); + if(GetAttackTarget(oSetUpTarget) != OBJECT_SELF) + { + GlobalMeleeTarget = oSetUpTarget; + iRemainingTargets = FALSE; + } + // If we have trouble using that one, IE they are attacking us, we + // just use normal methods. + } + // 1. AC + iMinimum = GetAIInteger(TARGETING_AC + MINIMUM); + // Do we do AC? And over one target... + // - iCnt2 is the total target stored in the last array. + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_AC); + iMaximum = GetAIInteger(TARGETING_AC + MAXIMUM); + + // We then set the array up in a new temp array. 1 = AC. + AI_TargetingArrayIntegerStore(i1, ARRAY_MELEE_ENEMY); + + // We loop through all the targets in the temp array (we also + // delete the temp array targets!) and set what ones we want to target + // based on AC. + // - Continue until iMinimum is reached. + // - Never go over iMaximum + // - After iMinimum we make sure the AC differences is not a major one of + // over 5. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum); + } + // Most others are similar (and all integer values so easy to check) + // 2. Phisical protections. + iMinimum = GetAIInteger(TARGETING_PHISICALS + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_PHISICALS); + iMaximum = GetAIInteger(TARGETING_PHISICALS + MAXIMUM); + // We then set the array up in a new temp array. 3 = phisicals. + AI_TargetingArrayIntegerStore(i3, ARRAY_MELEE_ENEMY); + // We loop as AC, basically. As it is stored based on the amount + // of DR offered, we set the limit to 6, so if everyone had + // 0 DR, and 1 had 5, it'd add the 5 for the hell of it. If everyone + // had 20, and one had 25 it'd take the 25 too, but not a 30. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i6, iMinimum, iMaximum); + } + // 4. BAB + iMinimum = GetAIInteger(TARGETING_BAB + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_BAB); + iMaximum = GetAIInteger(TARGETING_BAB + MAXIMUM); + // We then set the array up in a new temp array. 4 = BAB + AI_TargetingArrayIntegerStore(i4, ARRAY_MELEE_ENEMY); + // We loop as AC, basically. As it is BAB, IE how much chance + // they'll hit us (they might all hit on a 20, but it also shows + // who are the best fighters!). Set to 5, like AC. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum); + } + // 5. Hit Dice + iMinimum = GetAIInteger(TARGETING_HITDICE + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HITDICE); + iMaximum = GetAIInteger(TARGETING_HITDICE + MAXIMUM); + // We then set the array up in a new temp array. 5 = Hit dice + AI_TargetingArrayIntegerStore(i5, ARRAY_MELEE_ENEMY); + // We loop as AC. Hit Dice is even easier. We set the limit to + // a max of 4. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum); + } + // 6. Percent HP + iMinimum = GetAIInteger(TARGETING_HP_PERCENT + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_PERCENT); + iMaximum = GetAIInteger(TARGETING_HP_PERCENT + MAXIMUM); + // We then set the array up in a new temp array. 6 = % + AI_TargetingArrayIntegerStore(i6, ARRAY_MELEE_ENEMY); + // We loop as AC. Current Hit Points are easy, and are done + // by %ages. We set the % to 15 difference max. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i15, iMinimum, iMaximum); + } + // 7. Current HP + iMinimum = GetAIInteger(TARGETING_HP_CURRENT + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_CURRENT); + iMaximum = GetAIInteger(TARGETING_HP_CURRENT + MAXIMUM); + // We then set the array up in a new temp array. 7 = current HP + AI_TargetingArrayIntegerStore(i7, ARRAY_MELEE_ENEMY); + // We loop as AC. Current Hit points? Well, we set this limit to + // Our Hit Dice * 2. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, GlobalOurHitDice * i2, iMinimum, iMaximum); + } + // 8. Maximum HP + iMinimum = GetAIInteger(TARGETING_HP_MAXIMUM + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_MAXIMUM); + iMaximum = GetAIInteger(TARGETING_HP_MAXIMUM + MAXIMUM); + // We then set the array up in a new temp array. 8 = maximum + AI_TargetingArrayIntegerStore(i8, ARRAY_MELEE_ENEMY); + // We loop as AC. Max hit Hit points? Well, we set this limit to + // Our Hit Dice * 3. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, GlobalOurHitDice * i3, iMinimum, iMaximum); + } + // WHEW! + // Now we should have 1 or more chosen targets in ARRAY_MELEE_ARRAY. + // iRemaining Targets is the array size...we mearly choose a random + // one, or the first one if there is only one :-D + + // If only one, choose it + if(iRemainingTargets == i1) + { + GlobalMeleeTarget = GetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1); + } + // Check for 2+, if 0, we haven't got one to set! + else if(iRemainingTargets >= i2) + { + // Else Roll dice + iCnt = Random(iRemainingTargets) + i1; + // Set random target + GlobalMeleeTarget = GetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + IntToString(iCnt)); + } + } + } + // Else, it is oLastTarget that will be our target + else + { + GlobalMeleeTarget = oLastTarget; + } + + // If it is a new target, reset attacking counter to 0 + if(GlobalMeleeTarget != oLastTarget) + { + DeleteAIInteger(AI_MELEE_TURNS_ATTACKING); + } + +/*::////////////////////////////////////////////// + Ranged targeting + + This uses more stuff then melee targeting - for a start, range is optional! + + By default, it sets up all seen targets to the array, else all heard. +//::////////////////////////////////////////////*/ + + // Do we want to change melee targets? + oLastTarget = GetAIObject(AI_LAST_RANGED_TARGET); + + // iValid is the counter for attacking target X... + iBreak = GetAIInteger(AI_RANGED_TURNS_ATTACKING); + iBreak++; + SetAIInteger(AI_RANGED_TURNS_ATTACKING, iBreak); + // We set this to 0 if we our oLastTarget != GlobalMeleeTarget below changing. + + // Check % + // We use the same temp reset from before, because phisical attacking + // would be range or melee ;-) + // - Hey, do we even have a ranged weapon? + // - No valid override target + // - No valid override target + if(!GetIsObjectValid(GlobalRangedTarget) && GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED)) && + (AI_GetTargetSanityCheck(oLastTarget) || // Sanity check (dead, valid, ETC) + // And must pass a % roll + d100() <= GetBoundriedAIInteger(AI_RANGED_LAST_TO_NEW_TARGET_CHANCE, i20, i100) || + // OR last target attacked for X rounds + iBreak >= iMaxTurnsAttackingX)) + { + // 1. Set up all seen, else all heard, to the range array. + iCnt = i1; + iRemainingTargets = i0; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget)) + { + iRemainingTargets++; + SetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + IntToString(iRemainingTargets), oSetUpTarget); + // Get Next seen + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + } + // If we don't have any seen, used heard. + if(!iRemainingTargets) + { + iCnt = i1; + iRemainingTargets = i0; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget)) + { + iRemainingTargets++; + SetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + IntToString(iRemainingTargets), oSetUpTarget); + // Get Next seen + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt)); + } + } + // If not valid, set to melee target at the end + + // Do we have exactly 1 target? (iRemainingTargets == 1) + if(iRemainingTargets == i1) + { + // Then we make this our target and end it. + GlobalRangedTarget = GetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + s1); + } + // Else we set up using range ETC, if we have more then 1. + else if(iRemainingTargets) + { + // Range - they are already in range order. As it is used for + // spells, it uses the same function. + // Range - Used only for ranged phisical attacks and spell attacks (TARGETING_RANGE) + // - Melee - Range is used to see what it can reach, and the MAX and MIN are taken to account + // AC - Used only for phisical attacks (TARGETING_AC) + // Saving Throws - Used only for spell attacks (TARGETING_SAVES) + // Phisical Protections - Used for both spells and phisical attacks (TARGETING_PHISICALS) + // Base Attack Bonus - Used for both spells and phisical attacks (TARGETING_BAB) + // Hit Dice - Used for both spells and phisical attacks (TARGETING_HITDICE) + // HP Percent - Used for both spells and phisical attacks (TARGETING_HP_PERCENT) + // HP Current - Used for both spells and phisical attacks (TARGETING_HP_CURRENT) + // HP Maximum - Used for both spells and phisical attacks (TARGETING_HP_MAXIMUM) + // 0. Sneak attack check + if(GetHasFeat(FEAT_SNEAK_ATTACK)) + { + // We normally get the nearest enemy who is not attacking us, and + // actually stop! + AI_TargetingArrayIntegerStore(i9, ARRAY_RANGED_ENEMY); + + // Get the closest who isn't attacking us. + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + s1); + if(GetAttackTarget(oSetUpTarget) != OBJECT_SELF) + { + GlobalMeleeTarget = oSetUpTarget; + iRemainingTargets = FALSE; + } + // If we have trouble using that one, IE they are attacking us, we + // just use normal methods. + + // Delete temp array + AI_TargetingArrayDelete(ARRAY_TEMP_ARRAY); + } + iMinimum = GetAIInteger(TARGETING_RANGE + MINIMUM); + // 1. Do we do range? + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_RANGE); + iMaximum = GetAIInteger(TARGETING_RANGE + MAXIMUM); + + // We then set the array up in a new temp array. + AI_TargetingArrayDistanceStore(ARRAY_RANGED_ENEMY, ARRAY_TEMP_ARRAY); + // We loop range. Maximum one can be from another when got to the + // minimum is 5.0 M + iRemainingTargets = AI_TargetingArrayLimitTargetsFloat(ARRAY_RANGED_ENEMY, iTypeOfTarget, f5, iMinimum, iMaximum); + } + // 2. AC + iMinimum = GetAIInteger(TARGETING_AC + MINIMUM); + // Do we do AC? And over one target... + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_AC); + iMaximum = GetAIInteger(TARGETING_AC + MAXIMUM); + + // We then set the array up in a new temp array. 1 = AC. + AI_TargetingArrayIntegerStore(i1, ARRAY_RANGED_ENEMY); + + // We loop through all the targets in the temp array (we also + // delete the temp array targets!) and set what ones we want to target + // based on AC. + // - Continue until iMinimum is reached. + // - Never go over iMaximum + // - After iMinimum we make sure the AC differences is not a major one of + // over 5. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum); + } + // Most others are similar (and all integer values so easy to check) + // 3. Phisical protections. + iMinimum = GetAIInteger(TARGETING_PHISICALS + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_PHISICALS); + iMaximum = GetAIInteger(TARGETING_PHISICALS + MAXIMUM); + // We then set the array up in a new temp array. 3 = phisicals. + AI_TargetingArrayIntegerStore(i3, ARRAY_RANGED_ENEMY); + // We loop as AC, basically. As it is stored based on the amount + // of DR offered, limit is 0, not 6. Can't be any different. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum); + } + // 4. BAB + iMinimum = GetAIInteger(TARGETING_BAB + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_BAB); + iMaximum = GetAIInteger(TARGETING_BAB + MAXIMUM); + // We then set the array up in a new temp array. 4 = BAB + AI_TargetingArrayIntegerStore(i4, ARRAY_RANGED_ENEMY); + // We loop as AC, basically. As it is BAB, IE how much chance + // they'll hit us (they might all hit on a 20, but it also shows + // who are the best fighters!). Set to 5, like AC. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum); + } + // 5. Hit Dice + iMinimum = GetAIInteger(TARGETING_HITDICE + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HITDICE); + iMaximum = GetAIInteger(TARGETING_HITDICE + MAXIMUM); + // We then set the array up in a new temp array. 5 = Hit dice + AI_TargetingArrayIntegerStore(i5, ARRAY_RANGED_ENEMY); + // We loop as AC. Hit Dice is even easier. We set the limit to + // a max of 4. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum); + } + // 6. Percent HP + iMinimum = GetAIInteger(TARGETING_HP_PERCENT + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_PERCENT); + iMaximum = GetAIInteger(TARGETING_HP_PERCENT + MAXIMUM); + // We then set the array up in a new temp array. 5 = Hit dice + AI_TargetingArrayIntegerStore(i6, ARRAY_RANGED_ENEMY); + // We loop as AC. Current Hit Points are easy, and are done + // by %ages. We set the % to 15 difference max. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i15, iMinimum, iMaximum); + } + // 7. Current HP + iMinimum = GetAIInteger(TARGETING_HP_CURRENT + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_CURRENT); + iMaximum = GetAIInteger(TARGETING_HP_CURRENT + MAXIMUM); + // We then set the array up in a new temp array. 5 = Hit dice + AI_TargetingArrayIntegerStore(i7, ARRAY_RANGED_ENEMY); + // We loop as AC. Current Hit points? Well, we set this limit to + // Our Hit Dice * 2. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, GlobalOurHitDice * i2, iMinimum, iMaximum); + } + // 8. Maximum HP + iMinimum = GetAIInteger(TARGETING_HP_MAXIMUM + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_MAXIMUM); + iMaximum = GetAIInteger(TARGETING_HP_MAXIMUM + MAXIMUM); + // We then set the array up in a new temp array. 5 = Hit dice + AI_TargetingArrayIntegerStore(i8, ARRAY_RANGED_ENEMY); + // We loop as AC. Max hit Hit points? Well, we set this limit to + // Our Hit Dice * 3. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, GlobalOurHitDice * i3, iMinimum, iMaximum); + } + // End narrowing down on ARRAY_RANGED_ENEMY + + // If only one, choose it + if(iRemainingTargets == i1) + { + GlobalRangedTarget = GetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + s1); + } + // Check for 2+, if 0, we haven't got one to set! + else if(iRemainingTargets >= i2) + { + // Else Roll dice + iCnt = Random(iRemainingTargets) + i1; + // Set random target + GlobalRangedTarget = GetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + IntToString(iCnt)); + } + } + } + // Else, it is oLastTarget that will be our target + else + { + GlobalRangedTarget = oLastTarget; + } + + // If not valid, set to melee target + if(!GetIsObjectValid(GlobalRangedTarget)) + { + GlobalRangedTarget = GlobalMeleeTarget; + } + // If it is a new target, reset attacking counter to 0 + if(GlobalRangedTarget != oLastTarget) + { + DeleteAIInteger(AI_RANGED_TURNS_ATTACKING); + } + +/*::////////////////////////////////////////////// + Spell targeting + + Spell targeting is similar to ranged targeting. It is only reset if + iResetTargets is true. + + We never actually set a target if they are totally immune to our spells, uses + AI_SpellResistanceImmune for the checks. +//::////////////////////////////////////////////*/ + + // Do we want to change melee targets? + oLastTarget = GetAIObject(AI_LAST_SPELL_TARGET); + + // iValid is the counter for attacking target X... + iBreak = GetAIInteger(AI_SPELL_TURNS_ATTACKING); + iBreak++; + SetAIInteger(AI_SPELL_TURNS_ATTACKING, iBreak); + // We set this to 0 if we our oLastTarget != GlobalMeleeTarget below changing. + + // - No valid override target + if(!GetIsObjectValid(GlobalSpellTarget) && + (AI_GetTargetSanityCheck(oLastTarget) || // Sanity check (dead, valid, ETC) + // Or the last target was immune to all our spells + GetLocalInt(oLastTarget, AI_SPELL_IMMUNE_LEVEL) >= i9 || + // OR must pass a % roll + d100() <= GetBoundriedAIInteger(AI_SPELL_LAST_TO_NEW_TARGET_CHANCE, i20, i100) || + // OR last target attacked for X rounds + iBreak >= iMaxTurnsAttackingX)) + { + // 1. Set up all seen, else all heard, to the range array. + iCnt = i1; + iRemainingTargets = i0; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget)) + { + // Totally immune to our spells - ignore! + if(!AI_SpellResistanceImmune(oSetUpTarget) && + GetLocalInt(GlobalSpellTarget, AI_SPELL_IMMUNE_LEVEL) < i9) + { + iRemainingTargets++; + SetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + IntToString(iRemainingTargets), oSetUpTarget); + } + // Get Next seen + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + } + // If we don't have any seen, used heard. + if(!iRemainingTargets) + { + iCnt = i1; + iRemainingTargets = i0; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt)); + while(GetIsObjectValid(oSetUpTarget)) + { + // Totally immune to our spells - ignore! + if(!AI_SpellResistanceImmune(oSetUpTarget) && + GetLocalInt(GlobalSpellTarget, AI_SPELL_IMMUNE_LEVEL) < i9) + { + iRemainingTargets++; + SetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + IntToString(iRemainingTargets), oSetUpTarget); + } + // Get Next seen + iCnt++; + oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt)); + } + } + // If not valid, its invalid! As this is the last thing, return iM1. + // This CAN happen! + // - If we have all targets naturally immune to spells via. spell reistance. + // we can set GlobalNormalSpellsNoEffectLevel to 10, below, to stop all spells + // - After this lot of things, we do check for validness and set to melee target if no one else + + // MELEE Targets get checked for LOS, so they should always move + // to people we should attack ABOVE + // Do we have exactly 1 target? (iRemainingTargets == 1) + if(iRemainingTargets == i1) + { + // Then we make this our target and end it. + GlobalSpellTarget = GetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + s1); + } + // Else we set up using range ETC, if we have more then 1. + else if(iRemainingTargets > i1) + { + // ARRAY_SPELL_ENEMY + // Similar to Ranged attacking for range setting :-D + + // Range - they are already in range order. As it is used for + // spells, it uses the same function. + + // PC, Mantals. + // Range - Used only for ranged phisical attacks and spell attacks (TARGETING_RANGE) + // - Melee - Range is used to see what it can reach, and the MAX and MIN are taken to account + // AC - Used only for phisical attacks (TARGETING_AC) + // Saving Throws - Used only for spell attacks (TARGETING_SAVES) + // Phisical Protections - Used for both spells and phisical attacks (TARGETING_PHISICALS) + // Base Attack Bonus - Used for both spells and phisical attacks (TARGETING_BAB) + // Hit Dice - Used for both spells and phisical attacks (TARGETING_HITDICE) + // HP Percent - Used for both spells and phisical attacks (TARGETING_HP_PERCENT) + // HP Current - Used for both spells and phisical attacks (TARGETING_HP_CURRENT) + // HP Maximum - Used for both spells and phisical attacks (TARGETING_HP_MAXIMUM) + + // -1. Is it a PC... + // - Type 10 + iMinimum = GetAIInteger(TARGETING_ISPC + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_ISPC); + iMaximum = GetAIInteger(TARGETING_ISPC + MAXIMUM); + // We then set the array up in a new temp array. 10 = PC. + AI_TargetingArrayIntegerStore(i10, ARRAY_SPELL_ENEMY); + // We loop and set up, with no difference in PC status, IE we should always choose those who are PCs. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum); + } + // 0. Mantals + // - Type 10 + iMinimum = GetAIInteger(TARGETING_ISPC + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_ISPC); + iMaximum = GetAIInteger(TARGETING_ISPC + MAXIMUM); + // We then set the array up in a new temp array. 10 = PC. + AI_TargetingArrayIntegerStore(i10, ARRAY_SPELL_ENEMY); + // We loop and set up, with no difference in PC status, IE we should always choose those who are PCs. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum); + } + iMinimum = GetAIInteger(TARGETING_RANGE + MINIMUM); + // 1. Do we do range? + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_RANGE); + iMaximum = GetAIInteger(TARGETING_RANGE + MAXIMUM); + + // We then set the array up in a new temp array + AI_TargetingArrayDistanceStore(ARRAY_SPELL_ENEMY, ARRAY_TEMP_ARRAY); + // We loop range. Maximum one can be from another when got to the + // minimum is 5.0 M + iRemainingTargets = AI_TargetingArrayLimitTargetsFloat(ARRAY_SPELL_ENEMY, iTypeOfTarget, f5, iMinimum, iMaximum); + } + // 2. Saving Throws + iMinimum = GetAIInteger(TARGETING_SAVES + MINIMUM); + // Do we do saves? And over one target... + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_SAVES); + iMaximum = GetAIInteger(TARGETING_SAVES + MAXIMUM); + + // We then set the array up in a new temp array. 2 = total saves. + AI_TargetingArrayIntegerStore(i2, ARRAY_SPELL_ENEMY); + + // Saves are basically a spells' AC, or at least may hamper + // spells' power. + // - difference of 4 + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum); + } + // Most others are similar (and all integer values so easy to check) + // 3. Phisical protections. + iMinimum = GetAIInteger(TARGETING_PHISICALS + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_PHISICALS); + iMaximum = GetAIInteger(TARGETING_PHISICALS + MAXIMUM); + // We then set the array up in a new temp array. 3 = phisicals. + AI_TargetingArrayIntegerStore(i3, ARRAY_SPELL_ENEMY); + // We loop as AC, basically. As it is stored based on the amount + // of DR offered, limit is 0, not 6. Can't be any different. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum); + } + // 4. BAB + iMinimum = GetAIInteger(TARGETING_BAB + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_BAB); + iMaximum = GetAIInteger(TARGETING_BAB + MAXIMUM); + // We then set the array up in a new temp array. 4 = BAB + AI_TargetingArrayIntegerStore(i4, ARRAY_SPELL_ENEMY); + // We loop as AC, basically. As it is BAB, IE how much chance + // they'll hit us (they might all hit on a 20, but it also shows + // who are the best fighters!). Set to 5, like AC. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum); + } + // 5. Hit Dice + iMinimum = GetAIInteger(TARGETING_HITDICE + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HITDICE); + iMaximum = GetAIInteger(TARGETING_HITDICE + MAXIMUM); + // We then set the array up in a new temp array. 5 = Hit dice + AI_TargetingArrayIntegerStore(i5, ARRAY_SPELL_ENEMY); + // We loop as AC. Hit Dice is even easier. We set the limit to + // a max of 4. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum); + } + // 6. Percent HP + iMinimum = GetAIInteger(TARGETING_HP_PERCENT + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_PERCENT); + iMaximum = GetAIInteger(TARGETING_HP_PERCENT + MAXIMUM); + // We then set the array up in a new temp array. 6 = % HP + AI_TargetingArrayIntegerStore(i6, ARRAY_SPELL_ENEMY); + // We loop as AC. Current Hit Points are easy, and are done + // by %ages. We set the % to 15 difference max. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i15, iMinimum, iMaximum); + } + // 7. Current HP + iMinimum = GetAIInteger(TARGETING_HP_CURRENT + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_CURRENT); + iMaximum = GetAIInteger(TARGETING_HP_CURRENT + MAXIMUM); + // We then set the array up in a new temp array. 7 = Current + AI_TargetingArrayIntegerStore(i7, ARRAY_SPELL_ENEMY); + // We loop as AC. Current Hit points? Well, we set this limit to + // Our Hit Dice * 2. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, GlobalOurHitDice * i2, iMinimum, iMaximum); + } + // 8. Maximum HP + iMinimum = GetAIInteger(TARGETING_HP_MAXIMUM + MINIMUM); + if(iMinimum && iRemainingTargets >= i2) + { + // If so, is it lowest or highest? + iTypeOfTarget = GetAIInteger(TARGETING_HP_MAXIMUM); + iMaximum = GetAIInteger(TARGETING_HP_MAXIMUM + MAXIMUM); + // We then set the array up in a new temp array. 8 = Maximum + AI_TargetingArrayIntegerStore(i8, ARRAY_SPELL_ENEMY); + // We loop as AC. Max hit Hit points? Well, we set this limit to + // Our Hit Dice * 3. + iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, GlobalOurHitDice * i3, iMinimum, iMaximum); + } + // End narrowing down on ARRAY_SPELL_ENEMY + + // If only one, choose it + if(iRemainingTargets == i1) + { + GlobalSpellTarget = GetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + s1); + } + // Check for 2+, if 0, we haven't got one to set! + else if(iRemainingTargets >= i2) + { + // Else Roll dice + iCnt = Random(iRemainingTargets - i1) + i1; + // Set random target + GlobalSpellTarget = GetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + IntToString(iCnt)); + } + } + } + // Else, it is oLastTarget that will be our target + else + { + GlobalSpellTarget = oLastTarget; + } + + // If not valid, set to melee target + if(!GetIsObjectValid(GlobalSpellTarget)) + { + GlobalSpellTarget = GlobalMeleeTarget; + } + // If it is a new target, reset attacking counter to 0 + if(GlobalSpellTarget != oLastTarget) + { + DeleteAIInteger(AI_SPELL_TURNS_ATTACKING); + } + + // Set the final objects we chose + SetAIObject(AI_LAST_MELEE_TARGET, GlobalMeleeTarget); + SetAIObject(AI_LAST_SPELL_TARGET, GlobalSpellTarget); + SetAIObject(AI_LAST_RANGED_TARGET, GlobalRangedTarget); + + // Set Global* things for melee target + GlobalRangeToMeleeTarget = GetDistanceToObject(GlobalMeleeTarget); + GlobalMeleeTargetAC = GetAC(GlobalMeleeTarget); + GlobalMeleeTargetBAB = GetBaseAttackBonus(GlobalMeleeTarget); + // Generic check. + // - Set to TRUE, we should always have GlobalMeleeTarget as valid + GlobalAnyValidTargetObject = TRUE;//GetIsObjectValid(GlobalMeleeTarget); + + // Sort immunities + AI_SortSpellImmunities(); + + // Set Global* things for spell target + //GlobalNormalSpellsNoEffectLevel + // - The level of spells which are not affected - IE, if set to 3, then + // spell levels 0, 1, 2, 3 will NOT be cast. Abilites are still used! + + // REMEMBER: + // - We still can cast summons, and friendly spells! + // - AOE spells may still affect someone in range + // - Breaches can breach natural spell resistance. + + // If they are spell resistance immune, we do not cast any normal spells + // against them, except if we are a mage class + if(AI_SpellResistanceImmune(GlobalSpellTarget)) + { + // Note: We set the breach level to max, 5, so that we use breaches + // to bring down the SR of an enemy! + GlobalDispelTargetHighestBreach = i5; + + // Class checks. If not a mage caster, we will not use spells + if(GlobalOurChosenClass != CLASS_TYPE_WIZARD && + GlobalOurChosenClass != CLASS_TYPE_SORCERER && + GlobalOurChosenClass != CLASS_TYPE_FEY) + { + // 10 means we do not want to cast any normal spells + GlobalNormalSpellsNoEffectLevel = i10; + } + else + { + // We will set this to 5, level 5 spells and under stopped, if + // we are a mage. + GlobalNormalSpellsNoEffectLevel = i5; + } + } + + // Set the value to 10 if we have a 90% or greater chance of failing spells + // due to any armor, or any spell failure stuff. + if(GetArcaneSpellFailure(OBJECT_SELF) >= i90 && + GlobalNormalSpellsNoEffectLevel < i9) + { + // Always set to 10 if it is an effect + if(AI_GetAIHaveEffect(GlobalEffectSpellFailure)) + { + GlobalNormalSpellsNoEffectLevel = i10; + } + // - If we have auto-still, Leave it as it is if we can still all spells automatically + else if(!GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_3) && + !GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_2) && + !GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_1)) + { + // Else do not cast as in armor + GlobalNormalSpellsNoEffectLevel = i10; + } + } + + // We may set GlobalNormalSpellsNoEffectLevel to 1-4 if the enemy has some + // extra spell immunities. + if(GlobalNormalSpellsNoEffectLevel < i9 && + // Global setting needed + (GlobalIntelligence >= i9 || + GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SPECIFIC_SPELL_IMMUNITY, AI_COMBAT_MASTER))) + { + // This check now includes natural effect level + iValue = AI_GetSpellLevelEffect(GlobalSpellTarget); + if(GlobalNormalSpellsNoEffectLevel < iValue) + { + GlobalNormalSpellsNoEffectLevel = iValue; + } + } + + GlobalSpellTargetWill = GetWillSavingThrow(GlobalSpellTarget); + GlobalSpellTargetFort = GetFortitudeSavingThrow(GlobalSpellTarget); + GlobalSpellTargetReflex = GetReflexSavingThrow(GlobalSpellTarget); + GlobalSpellTargetHitDice = GetHitDice(GlobalSpellTarget); + GlobalSpellTargetCurrentHitPoints = GetCurrentHitPoints(GlobalSpellTarget); + GlobalSeenSpell = GetObjectSeen(GlobalSpellTarget); + GlobalSpellTargetRace = GetRacialType(GlobalSpellTarget); + // Range + GlobalSpellTargetRange = GetDistanceToObject(GlobalSpellTarget); + + // Set up GlobalSummonLocation to a location between targets. + // our last hostile actor, if not in 5 M, or else us + // - Summon spells have a 8M, short, range. + oSetUpTarget = OBJECT_INVALID; + if(GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SUMMON_TARGETING, AI_COMBAT_MASTER)) + { + if(GetIsObjectValid(oLastHostile) && GetDistanceToObject(oLastHostile) <= f16) + { + oSetUpTarget = oLastHostile; + } + if(GetDistanceToObject(GlobalRangedTarget) <= f16) + { + oSetUpTarget = GlobalRangedTarget; + } + } + // Check summon target + if(GetIsObjectValid(oSetUpTarget)) + { + // Taken from bioware's summon allies - half way between the targets. + // Because we get a maximum range of 16, it means the range will be 8. + vector vTarget = GetPosition(oSetUpTarget); + vector vSource = GetPosition(OBJECT_SELF); + vector vDirection = vTarget - vSource; + float fDistance = VectorMagnitude(vDirection) / f2; + vector vPoint = VectorNormalize(vDirection) * fDistance + vSource; + GlobalSummonLocation = Location(GetArea(OBJECT_SELF), vPoint, DIRECTION_NORTH); + } + // Else self + else + { + GlobalSummonLocation = GetLocation(OBJECT_SELF); + } + + // Set dispel target. + if(GetSpawnInCondition(AI_FLAG_COMBAT_DISPEL_MAGES_MORE, AI_COMBAT_MASTER)) + { + // Sorcerors, to mages, to clerics, to druids. + GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_SORCERER, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + if(!GetIsObjectValid(GlobalDispelTarget)) + { + GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_WIZARD, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + if(!GetIsObjectValid(GlobalDispelTarget)) + { + GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_CLERIC, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + if(!GetIsObjectValid(GlobalDispelTarget)) + { + GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_DRUID, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + } + } + } + } + if(!GetIsObjectValid(GlobalDispelTarget)) + { + GlobalDispelTarget = GlobalSpellTarget; + } + // Set enchantments + AI_SetDispelableEnchantments(); + + // Delete everything + AI_TargetingArrayDelete(ARRAY_TEMP_ENEMIES); + AI_TargetingArrayDelete(ARRAY_TEMP_ALLIES); + AI_TargetingArrayDelete(ARRAY_TEMP_ARRAY); + AI_TargetingArrayDelete(ARRAY_MELEE_ENEMY); + AI_TargetingArrayDelete(ARRAY_RANGED_ENEMY); + AI_TargetingArrayDelete(ARRAY_SPELL_ENEMY); + + // FALSE lets us continue with the script. + // - GlobalAnyValidTargetObject is set to TRUE if we want to attack anything. + return FALSE; +}// END ALL TARGETING + + +/*:://///////////////////////////////////////////// +//:: Name AttemptHostileSkills +//:://///////////////////////////////////////////// + This will use empathy, taunt, and if set, pickpocketing. Most are random, and + checks are made.Heal is done in healing functions.Done against best melee target, + or closest seen/heard. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +// Uses iSkill on GlobalMeleeTarget +// - Fired from AI_AttemptHostileSkills. +void AI_ActionUseSkillOnMeleeTarget(int iSkill) +{ + // 42: "[DCR:Skill] Using agressive skill (+Attack). [Skill] " + IntToString(iSkill) + " [Enemy]" + GetName(GlobalMeleeTarget) + DebugActionSpeakByInt(42, GlobalMeleeTarget, iSkill); + // We turn off hiding/searching + AI_ActionTurnOffHiding(); + // Simple most damaging + // - Equip shield first + AI_EquipBestShield(); + ActionEquipMostDamagingMelee(GlobalMeleeTarget); + ActionUseSkill(iSkill, GlobalMeleeTarget); + ActionWait(f2); + ActionAttack(GlobalMeleeTarget); +} +int AI_AttemptHostileSkills() +{ + if(GlobalRangeToMeleeTarget < f4 && + !GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER) && + !SRA)// Spell ranged attacking = AI_FLAG_COMBAT_LONGER_RANGED_SPELLS_FIRST + { + // Handles it better like this... + int iEmpathyDC, iRace; + // Either: Not turned off, and intelligence >= 3, OR forced, and has it. + if((!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PICKPOCKETING, AI_OTHER_COMBAT_MASTER) && + GlobalIntelligence >= i3) || + GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PICKPOCKETING, AI_OTHER_COMBAT_MASTER)) + { + // Need appropriate level of skill, checked On Spawn, or overriden... + AI_ActionUseSkillOnMeleeTarget(SKILL_PICK_POCKET); + return TRUE; + } + // If we have 50% in taunt (a decent amount), and concentration ETC are OK...do it! + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_TAUNTING, AI_OTHER_COMBAT_MASTER) && + !GetLocalTimer(AI_TIMER_TAUNT)) + { + if(GlobalIntelligence >= i2 || + GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_TAUNTING, AI_OTHER_COMBAT_MASTER)) + { + // Random determine if we use it... + if(GetSkillRank(SKILL_TAUNT) + Random(i10) >= + GetSkillRank(SKILL_CONCENTRATION, GlobalMeleeTarget) + Random(i10)) + { + // If randomly used, we set a timer for longer, and attack with it. + SetLocalTimer(AI_TIMER_TAUNT, f24); + SpeakArrayString(AI_TALK_ON_TAUNT); + AI_ActionUseSkillOnMeleeTarget(SKILL_TAUNT); + return TRUE; + } + else // 2 rounds until next check... + { + SetLocalTimer(AI_TIMER_TAUNT, f12); + } + } + } + // Animal empathy. Int 3 + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_EMPATHY, AI_OTHER_COMBAT_MASTER) && + !GetLocalTimer(AI_TIMER_EMPATHY)) + { + if(GlobalIntelligence >= i3 || + GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_EMPATHY, AI_OTHER_COMBAT_MASTER)) + { + iEmpathyDC = i20; + iRace = GetRacialType(GlobalMeleeTarget); + // we add 4 (to make DC 24 + HD) if a special animal. R_T_ANIMAL is DC 20 + if(iRace == RACIAL_TYPE_BEAST || iRace == RACIAL_TYPE_MAGICAL_BEAST) + { + iEmpathyDC += i4; + } + else if(iRace != RACIAL_TYPE_ANIMAL) + { + // Else, if we are not a beast, magical beast, nor animal, + // we don't use it! + SetLocalTimer(AI_TIMER_EMPATHY, f18); + // - Last skill we can use, so just return FALSE. + return FALSE; + } + // We check our skill against it... + if((GetSkillRank(SKILL_ANIMAL_EMPATHY) + i10) >= (GetHitDice(GlobalMeleeTarget) + i20)) + { + // If randomly used, we set a timer for longer, and attack with it. + SetLocalTimer(AI_TIMER_EMPATHY, f24); + AI_ActionUseSkillOnMeleeTarget(SKILL_ANIMAL_EMPATHY); + return TRUE; + } + else // 2 rounds until next check... + { + SetLocalTimer(AI_TIMER_EMPATHY, f12); + } + } + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name CastCombatHostileSpells +//:://///////////////////////////////////////////// + This will cast all buffs needed, or wanted, before actual combat. + EG bulls strength for HTH, Cats grace for ranged and so on. Rages + here, else it may run out if we use spells, and other lower spells as well. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ +int AI_AttemptFeatCombatHostile() +{ + int iUseSpells = TRUE; + // Don't chance the use of broken paladin and ranger spells. + if(GlobalOurChosenClass == CLASS_TYPE_PALADIN || + GlobalOurChosenClass == CLASS_TYPE_RANGER) + iUseSpells = FALSE; + + if(iUseSpells) + { + // Of course, we use, or attempt to use, our Divine Domain Powers, if + // we have them! They are spells, of no talent (or no talent I want to check) + if(!AI_GetAIHaveSpellsEffect(GlobalHasDomainSpells)) + { + if(AI_ActionCastSpell(SPELLABILITY_BATTLE_MASTERY)) return TRUE; // STRENGTH domain + if(AI_ActionCastSpell(SPELLABILITY_DIVINE_STRENGTH)) return TRUE; // STRENGTH domain + if(AI_ActionCastSpell(SPELLABILITY_BATTLE_MASTERY)) return TRUE; // WAR domain + if(AI_ActionCastSpell(SPELLABILITY_DIVINE_TRICKERY)) return TRUE; // TRICKERY domain + } + // Special power. Not, I think, a domain one. + if(AI_ActionCastSpell(SPELLABILITY_ROGUES_CUNNING)) return TRUE; + + // These are good-ass strength spells. They are basically + // powerful combat-orientated, strenth-inducing ones :-D + // Divine Power: Category 10, benifical Enhance self. Lvl 4. + if(AI_ActionCastSpell(SPELL_DIVINE_POWER, SpellEnhSelf, OBJECT_SELF, i14, FALSE, ItemEnhSelf, PotionPro)) return TRUE; + + // We may cast cats grace, if they are 6M or over away, we have + // a ranged weapon equipped or we have lowish AC compared to HD. + if(GlobalOurAC < GlobalOurHitDice + i6 + Random(i4) || + GlobalRangeToMeleeTarget > f6 || + GetWeaponRanged(GlobalRightHandWeapon)) + { + // Include harper cats grace. + if(!AI_GetAIHaveSpellsEffect(GlobalHasCatsGraceSpell)) + { + // Cats Grace. Level 2 (Mage/Bard/Ranger). + d4() + 1 dexterity. + if(AI_ActionUseFeatOnObject(FEAT_HARPER_CATS_GRACE)) return TRUE; + if(AI_ActionCastSpell(SPELL_GREATER_CATS_GRACE, SpellEnhSinTar)) return TRUE; + if(AI_ActionCastSpell(SPELL_CATS_GRACE, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar)) return TRUE; + } + } + // Include blackguard bulls strength. + if(!AI_GetAIHaveSpellsEffect(GlobalHasBullsStrengthSpell)) + { + // Bulls Strength. Level 2 (Mage/Cleric/Bard/Paladin/Druid). + d4() + 1 strength. + if(AI_ActionCastSpell(SPELL_GREATER_BULLS_STRENGTH, SpellEnhSinTar)) return TRUE; + // Blackguard version + if(AI_ActionUseFeatOnObject(FEAT_BULLS_STRENGTH)) return TRUE; + if(AI_ActionCastSpell(SPELL_BULLS_STRENGTH, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar)) return TRUE; + } + + // Other helping spells, lower ranks. If we can't hit much, we cast this. + // - Some help more then others. + // - We ignore some if in HTH and have low HD compared to spell. + if((GlobalOurHitDice < i15 || GlobalMeleeAttackers < i1) && + // We only add 15, as we don't apply strength ETC. at the mo. + (GlobalOurBaseAttackBonus - i15 < GlobalMeleeTargetAC) && + (!AI_GetAIHaveSpellsEffect(GlobalHasAidingSpell))) + { + // Prayer. Level 3 (Paladin/Cleric). +1 Attack, damage, saves to all allies in 10M Including self. + if(AI_ActionCastSpell(SPELL_PRAYER, SpellEnhAre, OBJECT_SELF, i13, FALSE, ItemEnhAre)) return TRUE; + if(GlobalOurHitDice < i14) + { + // Aid. Level 2 (Cleric/Paladin) 3 (Ranger) +1 Attack Rolls, +1d8 HP + if(AI_ActionCastSpell(SPELL_AID, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar)) return TRUE; + if(GlobalOurHitDice < i12) + { + // Bless. Level 1 (Cleric/Paladin). +1 Saves, +1 Damage, to allies in area. + if(AI_ActionCastSpell(SPELL_BLESS, SpellEnhSinTar, OBJECT_SELF, i11, FALSE, ItemEnhSinTar)) return TRUE; + } + } + } + // Attack bonus spells/feats, that directly add to our attack bonus. + + // Divine might adds charisma bonus to Attack. + if(GetHasFeat(FEAT_TURN_UNDEAD)) + { + // +Cha bonus to attack bonus. + if(AI_ActionUseFeatOnObject(FEAT_DIVINE_MIGHT)) return TRUE; + } + + // We now cast the spells which will affect our weapon (if we have one!) + // Only cast these if we have a valid right hand weapon. + if(GetIsObjectValid(GlobalRightHandWeapon) && + // Casts on melee weapons only, except if set to always use range + (!GetWeaponRanged(GlobalRightHandWeapon) || + GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER)) && + // We do it VIA. the medium of one integer. + !AI_GetAIHaveSpellsEffect(GlobalHasWeaponHelpSpell)) + { + // Cast best to worst, only one in affect at once, however. + + // Blackstaff. Great stuff for a STAFF ONLY. + if(GetBaseItemType(GlobalRightHandWeapon) == BASE_ITEM_QUARTERSTAFF) + { + // Blackstaff. Level 8 (Mage). Adds +4 enhancement bonus, On Hit: Dispel. + if(AI_ActionCastSpell(SPELL_BLACKSTAFF, SpellEnhSinTar, GlobalRightHandWeapon, i18, FALSE, ItemEnhSinTar)) return TRUE; + } + // Greater Magical Weapon - up to +5 + // Greater Magic Weapon. Level 3 (Mage/Bard/Paladin) 4 (Cleric). Grants a +1/3 caster levels (to +5). + if(AI_ActionCastSpell(SPELL_GREATER_MAGIC_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i14, FALSE, ItemEnhSinTar)) return TRUE; + + // Blade Thurst - cast on any weapon. No bother to check type. + // Blade Thurst. Level 3 (Ranger). +3 Damage for a slashing weapon. + if(AI_ActionCastSpell(SPELL_BLADE_THIRST, SpellEnhSinTar, GlobalRightHandWeapon, i13, FALSE, ItemEnhSinTar)) return TRUE; + + // Dark Fire. Level 3 (Cleric). 1d6 fire damage +1/level to a maximum of +10. + if(AI_ActionCastSpell(SPELL_DARKFIRE, SpellEnhSinTar, GlobalRightHandWeapon, i13, FALSE, ItemEnhSinTar)) return TRUE; + + // Flame Weapon. Level 2 (Mage). 1d4 fire damage +1/level to a maximum of +10. + if(AI_ActionCastSpell(SPELL_FLAME_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i12, FALSE, ItemEnhSinTar)) return TRUE; + + // Not if we are already keen :-) + if(!GetItemHasItemProperty(GlobalRightHandWeapon, ITEM_PROPERTY_KEEN)) + { + // Keen Edge. Level 1 (Bard/Mage/Paladin). Adds the Keen property to a weapon + if(AI_ActionCastSpell(SPELL_BLADE_THIRST, SpellEnhSinTar, GlobalRightHandWeapon, i11, FALSE, ItemEnhSinTar)) return TRUE; + } + + // Magic weapon - +1 + if(!GetItemHasItemProperty(GlobalRightHandWeapon, ITEM_PROPERTY_ENHANCEMENT_BONUS)) + { + // Magic Weapon. Level 1 (Bard/Cleric/Paladin/Ranger/Mage). +1 Enchantment to melee weapon. + if(AI_ActionCastSpell(SPELL_MAGIC_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i11, FALSE, ItemEnhSinTar)) return TRUE; + } + + // Bless weapon + if(GetRacialType(GlobalMeleeTarget) == RACIAL_TYPE_UNDEAD) + { + // Bless weapon. Level 1 (Paladin). +1 Attack Bonus, +2d6 Damage to melee weapon VS undead + if(AI_ActionCastSpell(SPELL_MAGIC_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i11, FALSE, ItemEnhSinTar)) return TRUE; + } + } + } + // We also will throw grenades here, if we are at a cirtain HD - 5 or under, + // because we may have ignored them in the spells part. + + // Try grenades - we always throw these. They are about level 1 standard of DC's + // and effects. Not too bad, when NPC's get a chance to use them! :-) + if(GlobalOurHitDice <= i5 || + // Will throw at range if under 10 HD + (GlobalRangeToMeleeTarget > f5 && GlobalOurHitDice < i10)) + { + if(AI_AttemptGrenadeThrowing(GlobalMeleeTarget)) return TRUE; + } + + // Here, we use all potions if set too... + if(GetSpawnInCondition(AI_FLAG_COMBAT_USE_ALL_POTIONS, AI_COMBAT_MASTER)) + { + int iUsed = FALSE; + // Check protection potion + if(PotionPro > FALSE && !GetHasSpellEffect(PotionPro)) + { + iUsed = PotionPro; + ActionUseTalentOnObject(tPotionPro, OBJECT_SELF); + } + // Check enhancement potion + else if(PotionEnh > FALSE && !GetHasSpellEffect(PotionEnh)) + { + iUsed = PotionEnh; + ActionUseTalentOnObject(tPotionEnh, OBJECT_SELF); + } + // Check conditional potion + else if(PotionCon > FALSE && !GetHasSpellEffect(PotionCon)) + { + iUsed = PotionCon; + ActionUseTalentOnObject(tPotionCon, OBJECT_SELF); + } + if(iUsed > FALSE) + { + // 43 "[DCR:Pre-Melee Spells] All Potions Using. [Spell ID] " + IntToString(iUsed) + DebugActionSpeakByInt(43, OBJECT_INVALID, iUsed); + return TRUE; + } + } + + // Rage - check effects via the set effects.. + if(!AI_GetAIHaveSpellsEffect(GlobalHasRageSpells)) + { + // Rage: either: + // +6, to STR, CON, will. -2 AC. + // or +4, to STR, CON, will. -2 AC. + if(AI_ActionUseFeatOnObject(FEAT_BARBARIAN_RAGE)) return TRUE; + // Blood frenzy. Level 2 (druid) as rage, for the most part. +2, to STR, CON, Will, -1 AC though. + if(AI_ActionCastSpell(SPELL_BLOOD_FRENZY, SpellOtherSpell, OBJECT_SELF, i12)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_RAGE_5)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_RAGE_4)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_FEROCITY_3)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_INTENSITY_3)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_RAGE_3)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_INTENSITY_2)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_FEROCITY_2)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_INTENSITY_1)) return TRUE; + if(AI_ActionCastSpell(SPELLABILITY_FEROCITY_1)) return TRUE; + } + // Cast expeditious retreat if we want to get into combat quicker + if(GlobalRangeToMeleeTarget >= f20 && + !GetWeaponRanged(GlobalRightHandWeapon) && + !AI_GetAIHaveEffect(GlobalEffectHaste) && + !GetHasSpellEffect(SPELL_EXPEDITIOUS_RETREAT)) + { + // Expeditious Retreat. Level 1 (Bard/Mage). +150% movement speed. + if(AI_ActionCastSpell(SPELL_EXPEDITIOUS_RETREAT, SpellEnhSelf, OBJECT_SELF, i11, FALSE, ItemEnhSelf, PotionEnh)) return TRUE; + } + // Cast true strike if we are RIGHT near the melee target, or are using + // a ranged weapon + if((GlobalRangeToMeleeTarget < f3 || + GetWeaponRanged(GlobalRightHandWeapon)) && + !GetHasSpellEffect(SPELL_TRUE_STRIKE)) + { + // True Strike. Level 1 (Mage). +20 attack bonus for 9 seconds (IE about 1, or 2, attacks) + if(AI_ActionCastSpell(SPELL_TRUE_STRIKE, SpellEnhSelf, OBJECT_SELF, i11, FALSE, ItemEnhSelf, PotionEnh)) + { + // 44: "[DCR:Pre-Melee Spells] True Strike Emptive attack [Target] " + GetName(GlobalMeleeTarget) + DebugActionSpeakByInt(44, GlobalMeleeTarget); + // Add attack to end of action queue. Should do this next round + // anyway + if(GlobalRangeToMeleeTarget <= f4) + { + ActionEquipMostDamagingMelee(GlobalMeleeTarget); + } + else + { + ActionEquipMostDamagingRanged(GlobalMeleeTarget); + } + ActionAttack(GlobalMeleeTarget); + return TRUE; + } + } + return FALSE; +} +/*:://///////////////////////////////////////////// +//:: Name PolyMorph +//:://///////////////////////////////////////////// + If we are not affected by polymorph, going down from + best to worst, we will polymorph. Use after combat buffs, + after spells (although because some are random, this + may fire when they still have some) and before we attack. +//:://///////////////////////////////////////////// +//:: Created By: Jasperre +//::////////////////////////////////////////////*/ + +// This will cheat-cast iSpell at oTarget. Note that we will know if we have it +// by checking what appearance we have. +void AI_ActionCastShifterSpell(int iSpell, object oTarget = OBJECT_SELF) +{ + // Cheat cast the spell. We know we must have it. + ActionCastSpellAtObject(iSpell, oTarget, METAMAGIC_NONE, TRUE); +} +// This willcast iFirst -> iFirst + iAmount polymorph spell. It will check +// if we have iMaster (Either by feat or spell, depending on iFeat). +// TRUE if we polymorph. +int AI_ActionPolymorph(int iMaster, int iFirstSpell, int iAmount, int iFeat = FALSE, int iRemove = TRUE) +{ + if((iFeat && GetHasFeat(iMaster)) || + (!iFeat && GetHasSpell(iMaster))) + { + // Randomise + // EG: Got from 300 to 303, so iAmount is 3 and we input 300 as the start one. + int iCast = iFirstSpell + Random(iAmount); + + // Debug + // 11: "[DCR:Casting] SubSpecialSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break; + DebugActionSpeakByInt(11, OBJECT_SELF, iCast); + + // If a spell, we concentration check it :-) + if(!iFeat) + { + AI_AttemptConcentrationCheck(GlobalSpellTarget); + } + + // Cast it + ActionCastSpellAtObject(iCast, OBJECT_SELF, METAMAGIC_NONE, TRUE); + + if(iRemove) + { + // Decrement one or the other + if(iFeat) + { + // Feat + DecrementRemainingFeatUses(OBJECT_SELF, iMaster); + } + else + { + // Spell + DecrementRemainingSpellUses(OBJECT_SELF, iMaster); + } + } + // Add Action Attack melee target :-D + ActionAttack(GlobalMeleeTarget); + return TRUE; + } + return FALSE; +} +// Will polymorph Self if not already so. Will return TRUE if it casts best on self. +int AI_AttemptPolyMorph() +{ + if(!GetSpawnInCondition(AI_FLAG_OTHER_NO_POLYMORPHING, AI_OTHER_MASTER) && + // We don't polymorph as an archer. + !GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER)) + { + // Spells that are tensers transformation and polymorth here. + // Will cast 100% on all shapechanges. Need to make it check for any spells. + // Check values needed + if(!AI_GetAIHaveEffect(GlobalEffectPolymorph)) + { + // Epic shapechanging feats + // - Dragon - just different breaths. + // 707 Greater_Wild_Shape_Red_dragon + // 708 Greater_Wild_Shape_Blue_dragon + // 709 Greater_Wild_Shape_Green_dragon + if(AI_ActionPolymorph(AI_FEAT_EPIC_WILD_SHAPE_DRAGON, 707, 3, TRUE)) return TRUE; + // Construct feat + // 738 Construct_Shape_StoneGolem + // 739 Construct_Shape_DemonFleshGolem + // 740 Construct_Shape_IronGolem + if(AI_ActionPolymorph(AI_FEAT_EPIC_CONSTRUCT_SHAPE, 738, 3, TRUE)) return TRUE; + // Outsider shape + // 733 Outsider_Shape_Azer + // 734 Outsider_Shape_Rakshasa + // 735 Outsider_Shape_DeathSlaad + if(AI_ActionPolymorph(AI_FEAT_EPIC_OUTSIDER_SHAPE, 733, 3, TRUE)) return TRUE; + // Undead - any of them + // 704 Undead_Shape_risen_lord + // 705 Undead_Shape_Vampire + // 706 Undead_Shape_Spectre + if(AI_ActionPolymorph(AI_FEAT_EPIC_WILD_SHAPE_UNDEAD, 704, 3, TRUE)) return TRUE; + // Shapechange + // 392 Shapechange_RED_DRAGON + // 393 Shapechange_FIRE_GIANT + // 394 Shapechange_BALOR + // 395 Shapechange_DEATH_SLAAD + // 396 Shapechange_IRON_GOLEM + if(AI_ActionPolymorph(SPELL_SHAPECHANGE, 392, 5)) return TRUE; + // Druid feat. Elements. + // 397 Elemental_Shape_FIRE + // 398 Elemental_Shape_WATER + // 399 Elemental_Shape_EARTH + // 400 Elemental_Shape_AIR + if(AI_ActionPolymorph(AI_FEAT_EPIC_DRUID_INFINITE_ELEMENTAL_SHAPE, 397, 4, TRUE, FALSE)) return TRUE; + if(AI_ActionPolymorph(FEAT_ELEMENTAL_SHAPE, 397, 4, TRUE)) return TRUE; + // Wildshape 4 is next best + // - Infinite and normal shapes + // - Not in any order (doh!) + // 671 Greater_Wild_Shape_Beholder + // 679 Greater_Wild_Shape_Medusa + // 691 Greater_Wild_Shape_Mindflayer + // 694 Greater_Wild_Shape_DireTiger + // Random choose one + int iSpell = 671; + switch(d4()) + { + case i1: iSpell = 671; break; + case i2: iSpell = 679; break; + case i3: iSpell = 691; break; + case i4: iSpell = 694; break; + } + if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_4, iSpell, FALSE, TRUE, FALSE)) return TRUE; + if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_4, iSpell, FALSE, TRUE)) return TRUE; + // Humanoid shape + // 682 Humanoid_Shape_Drow + // 683 Humanoid_Shape_Lizardfolk + // 684 Humanoid_Shape_KoboldAssa + if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_HUMANOID_SHAPE, 682, 3, TRUE, FALSE)) return TRUE; + if(AI_ActionPolymorph(AI_FEAT_HUMANOID_SHAPE, 682, 3, TRUE)) return TRUE; + // 3 - Infinite and normal shapes + // 3 - Not in any order (doh!) + // 670 Greater_Wild_Shape_Basilisk + // 673 Greater_Wild_Shape_Drider + // 674 Greater_Wild_Shape_Manticore + // Random choose one + iSpell = 670; + switch(d3()) + { + case i1: iSpell = 670; break; + case i2: iSpell = 673; break; + case i3: iSpell = 674; break; + } + if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_3, iSpell, FALSE, TRUE, FALSE)) return TRUE; + if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_3, iSpell, FALSE, TRUE)) return TRUE; + // 2 - Infinite and normal shapes + // 2 - Not in any order (doh!) + // 672 Greater_Wild_Shape_Harpy + // 678 Greater_Wild_Shape_Gargoyle + // 680 Greater_Wild_Shape_Minotaur + if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_2, iSpell, FALSE, TRUE, FALSE)) return TRUE; + if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_2, iSpell, FALSE, TRUE)) return TRUE; + // 1 - Infinite and normal shapes - In order + // 658 Greater_Wild_Shape_Wyrmling_Red + // 659 Greater_Wild_Shape_Wyrmling_Blue + // 660 Greater_Wild_Shape_Wyrmling_Black + // 661 Greater_Wild_Shape_Wyrmling_White + // 662 Greater_Wild_Shape_Wyrmling_Green + if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_1, 658, 5, TRUE, FALSE)) return TRUE; + if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_1, 658, 5, TRUE)) return TRUE; + + // We can have items for this polymorph spell :-) + if(AI_ActionCastSpell(SPELL_TENSERS_TRANSFORMATION, SpellEnhSelf, OBJECT_SELF, i16, ItemEnhSelf, PotionEnh)) return TRUE; + + // Animal wildshape + // 401 Wild_Shape_BROWN_BEAR + // 402 Wild_Shape_PANTHER + // 403 Wild_Shape_WOLF + // 404 Wild_Shape_BOAR + // 405 Wild_Shape_BADGER + if(AI_ActionPolymorph(AI_FEAT_EPIC_DRUID_INFINITE_WILDSHAPE, 401, 5, TRUE, FALSE)) return TRUE; + if(AI_ActionPolymorph(FEAT_WILD_SHAPE, 401, 5, TRUE)) return TRUE; + + // Shapechange into - Troll, Pixie, Uber Hulk, Giant Spider, Zombie + // 387 Polymorph_GIANT_SPIDER + // 388 Polymorph_TROLL + // 389 Polymorph_UMBER_HULK + // 390 Polymorph_PIXIE + // 391 Polymorph_ZOMBIE + if(AI_ActionPolymorph(SPELL_POLYMORPH_SELF, 387, 5, TRUE)) return TRUE; + } + else /*Else we have it*/if(d10() <= i6) + { + // The special abilities VIA. Shapechanger! + // - We check our appearance and cast as appropriately. + // - 60% base chance of casting an ability or spell. + + switch(GlobalOurAppearance) + { + case APPEARANCE_TYPE_ELEMENTAL_WATER: + case APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER: + { + // We can cast Pulse Drown unlimited times/day. + // Only use if above 50HP, and enemy has less then 20 Fortitude + // and is within 4M + if(GlobalRangeToMeleeTarget < f4 && GlobalOurCurrentHP > i50 && + GetFortitudeSavingThrow(GlobalMeleeTarget) < i18) + { + AI_ActionCastShifterSpell(SPELLABILITY_PULSE_DROWN); + return TRUE; + } + return FALSE; + } + break; + case APPEARANCE_TYPE_ELEMENTAL_AIR: + case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER: + { + // Can use Pulse: Whirlwind unlimited times/day. + // DC 14 or knockdown + some damage. + if(GlobalRangeToMeleeTarget < f4 && GetReflexSavingThrow(GlobalMeleeTarget) < i15) + { + AI_ActionCastShifterSpell(SPELLABILITY_PULSE_WHIRLWIND); + return TRUE; + } + return FALSE; + } + break; + // Wyrmling Breath Attacks are in the default dragon behaviour + // (we check for dragon appearance and check talents) + case APPEARANCE_TYPE_MANTICORE: + { + // Can use Spike Attack (Greater Wild Shape Version) at + // some reflex save or other. + if(GlobalRangeToMeleeTarget < f10 && + GetReflexSavingThrow(GlobalMeleeTarget) < (i18+ d6())) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_GWILDSHAPE_SPIKES, GlobalMeleeTarget); + return TRUE; + } + return FALSE; + } + break; + // Drow/Drider : Don't bother with darkness. + case 491:// Harpy! + { + // Harpysong - charm enemies. Will saving throw. + if(GlobalRangeToMeleeTarget < f4 && + GetWillSavingThrow(GlobalMeleeTarget) < (i14 + d4())) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_HARPYSONG); + return TRUE; + } + return FALSE; + } + break; + case APPEARANCE_TYPE_BASILISK: + case APPEARANCE_TYPE_MEDUSA: + { + // Limited petrify gaze attack + // Pretty cool. + if(GetLocalInt(OBJECT_SELF,"X2_GWILDSHP_LIMIT_" + IntToString(AI_SPELLABILITY_GWILDSHAPE_STONEGAZE)) && + GlobalRangeToMeleeTarget < f4 && + GetFortitudeSavingThrow(GlobalMeleeTarget) < (i14 + d4())) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_GWILDSHAPE_STONEGAZE, GlobalMeleeTarget); + return TRUE; + } + return FALSE; + } + break; + case 413: // Mind Flayer + { + // Psionic Inertial Barrier ability - unlimited uses. + if(!GetHasSpellEffect(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER)) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER); + return TRUE; + } + // Else, we have Mind Blast. Limited uses, but stuns! + else if(!GetHasSpellEffect(AI_SPELLABILITY_GWILDSHAPE_MINDBLAST, GlobalMeleeTarget) && + GetLocalInt(OBJECT_SELF,"X2_GWILDSHP_LIMIT_" + IntToString(AI_SPELLABILITY_GWILDSHAPE_MINDBLAST)) && + GlobalRangeToMeleeTarget < f8) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_GWILDSHAPE_MINDBLAST); + return TRUE; + } + return FALSE; + } + break; + // Dragons (ancient) checked for breath via. normal means. + case APPEARANCE_TYPE_VAMPIRE_FEMALE: // Vampires + case APPEARANCE_TYPE_VAMPIRE_MALE: // Vampires + { + // Limited Domination Gazes. + if(GetLocalInt(OBJECT_SELF, "X2_GWILDSHP_LIMIT_" + IntToString(AI_SPELLABILITY_VAMPIRE_DOMINATION_GAZE)) && + !GetHasSpellEffect(AI_SPELLABILITY_VAMPIRE_DOMINATION_GAZE, GlobalMeleeTarget) && + GetWillSavingThrow(GlobalMeleeTarget) < (i14 + d4()) && + // This is a simple check for "have we got a dominated guy already" + !AI_GetSpellTargetImmunity(GlobalImmunityDomination)) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_VAMPIRE_DOMINATION_GAZE, GlobalMeleeTarget); + return TRUE; + } + return FALSE; + } + break; + case APPEARANCE_TYPE_SPECTRE: + { + // Unlimited invisibility, and unlimited "spectre attack". + // 60% chance of using the spectre attack. + if(d10() <= i6 && GetFortitudeSavingThrow(GlobalMeleeTarget) < (i14 + d10()) && + !GetIsImmune(GlobalMeleeTarget, IMMUNITY_TYPE_NEGATIVE_LEVEL)) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_SHIFTER_SPECTRE_ATTACK, GlobalMeleeTarget); + return TRUE; + } + // Else invisiblity + if(!AI_GetAIHaveEffect(GlobalEffectInvisible)) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_VAMPIRE_INVISIBILITY); + return TRUE; + } + return FALSE; + } + break; + case APPEARANCE_TYPE_WILL_O_WISP: + { + // Unlimited invisibility + if(!AI_GetAIHaveEffect(GlobalEffectInvisible)) + { + AI_ActionCastShifterSpell(SPELL_INVISIBILITY); + return TRUE; + } + return FALSE; + } + break; + case 428:// Azer man + case 429:// Azer female + { + // Unlimited fire attacks. + // Burning hands and azer blast. + // 80% chance of azer blast + if(GetReflexSavingThrow(GlobalMeleeTarget) < (i14 + d6()) && + d10() <= i8) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_AZER_FIRE_BLAST, GlobalMeleeTarget); + return TRUE; + } + // Else burning hands + if(GetReflexSavingThrow(GlobalMeleeTarget) < (i12 + d4())) + { + AI_ActionCastShifterSpell(SPELL_BURNING_HANDS, GlobalMeleeTarget); + return TRUE; + } + return FALSE; + } + break; + case APPEARANCE_TYPE_SLAAD_DEATH: + { + // Unlimited spittle attacks. Just need the base 60% chance above. + AI_ActionCastShifterSpell(AI_SPELLABILITY_SLAAD_CHAOS_SPITTLE, GlobalMeleeTarget); + return TRUE; + } + break; + case APPEARANCE_TYPE_RAKSHASA_TIGER_FEMALE: + case APPEARANCE_TYPE_RAKSHASA_TIGER_MALE: + { + // Unlimited spells - 3. + // - Dispel Magic + // - Ice Storm + // - Mestils Acid Breath. + // Randomise each one. Don't bother checking saves. + if(d10() <= i6) + { + AI_ActionCastShifterSpell(SPELL_DISPEL_MAGIC, GlobalMeleeTarget); + return TRUE; + } + else if(d10() <= i6) + { + AI_ActionCastShifterSpell(VFX_FNF_ICESTORM, GlobalMeleeTarget); + return TRUE; + } + else + { + AI_ActionCastShifterSpell(SPELL_MESTILS_ACID_BREATH, GlobalMeleeTarget); + return TRUE; + } + return FALSE; + } + break; + case APPEARANCE_TYPE_GOLEM_IRON: + { + // Unlimited spells Iron Golem Breath. + if(GetFortitudeSavingThrow(GlobalMeleeTarget) < i20) + { + AI_ActionCastShifterSpell(SPELLABILITY_GOLEM_BREATH_GAS, GlobalMeleeTarget); + return TRUE; + } + return FALSE; + } + break; + case APPEARANCE_TYPE_GOLEM_STONE: + { + // Unlimited spells: Throw rocks + if(GetReflexSavingThrow(GlobalMeleeTarget) < i20 + d6()) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_GIANT_HURL_ROCK, GlobalMeleeTarget); + return TRUE; + } + return FALSE; + } + break; + case 302:// Kobold (assassin) + { + // Unlimited invisibility + // Unlimited invisibility + if(!AI_GetAIHaveEffect(GlobalEffectInvisible)) + { + AI_ActionCastShifterSpell(AI_SPELLABILITY_VAMPIRE_INVISIBILITY); + return TRUE; + } + return FALSE; + } + break; + } + } + } + return FALSE; +} +// Returns TRUE if we counterspell GlobalCounterspellTarget, only does it +// if we have Dispels, and if set to want to be in a group, we are in one :-) +int AI_AttemptCounterSpell() +{ + // Check for 5+ allies if counter spell in group. If <= 4, return FALSE + if(GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_ONLY_IN_GROUP, AI_COMBAT_MASTER) && + GlobalTotalAllies <= i4) + { + return FALSE; + } + // Need a dispel spell + if(!(GetHasSpell(SPELL_MORDENKAINENS_DISJUNCTION) || + GetHasSpell(SPELL_GREATER_DISPELLING) || + GetHasSpell(SPELL_DISPEL_MAGIC) || + GetHasSpell(SPELL_LESSER_DISPEL))) + { + return FALSE; + } + object oLoopTarget, oCounterspellTarget; + int iCnt, iCasterLevels, iHighestLevels; + float fDistance; + // Try and get a Arcane caster to counter + if(GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_ARCANE, AI_COMBAT_MASTER)) + { + // Loop seen enemies - must be within 20M and valid + iCnt = i1; + oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oLoopTarget) && fDistance <= f20) + { + // Check caster levels + iCasterLevels = GetLevelByClass(CLASS_TYPE_WIZARD, oLoopTarget) + + GetLevelByClass(CLASS_TYPE_SORCERER, oLoopTarget) + + GetLevelByClass(CLASS_TYPE_BARD, oLoopTarget); + // Check if higher. + if(iCasterLevels > iHighestLevels) + { + iHighestLevels = iCasterLevels; + oCounterspellTarget = oLoopTarget; + } + // Get next target + iCnt++; + oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + } + } + // If not valid, might check divine + if(!GetIsObjectValid(oCounterspellTarget) && + iHighestLevels >= GlobalOurHitDice / i3 && + GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_DIVINE, AI_COMBAT_MASTER)) + { + // Loop seen enemies - must be within 20M and valid + iHighestLevels = FALSE; + iCnt = i1; + oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + while(GetIsObjectValid(oLoopTarget) && fDistance <= f20) + { + // Check caster levels + iCasterLevels = GetLevelByClass(CLASS_TYPE_CLERIC, oLoopTarget) + + GetLevelByClass(CLASS_TYPE_DRUID, oLoopTarget); + // Check if higher. + if(iCasterLevels > iHighestLevels) + { + iHighestLevels = iCasterLevels; + oCounterspellTarget = oLoopTarget; + } + // Get next target + iCnt++; + oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt)); + } + } + // Check if valid + if(GetIsObjectValid(oCounterspellTarget)) + { + // 45: "[DCR:CounterSpell] Counterspelling. [Target] " + GetName(oCounterspellTarget) + DebugActionSpeakByInt(45, oCounterspellTarget); + AI_SetMeleeMode(ACTION_MODE_COUNTERSPELL); + ActionCounterSpell(oCounterspellTarget); + return TRUE; + } + return FALSE; +} +// This will, in most occasion, ClearAllActions. +// If it does NOT, it returns FALSE, if we are doing something more important, +// and we perform that action again (or carry on doing it). +int AI_StopWhatWeAreDoing() +{ + // - New wrappered function + if(GetIsPerformingSpecialAction()) + { + return FALSE; + } + // See if we need ClearAllActions(TRUE) for HIPS + if(GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT) && + GetStealthMode(OBJECT_SELF) == STEALTH_MODE_DISABLED && + !GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_HIDING, AI_OTHER_COMBAT_MASTER)) + { + // Check for nearest person with trueseeing (which pierces hiding) + if(!GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_TRUE_SEEING, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY))) + { + ClearAllActions(TRUE); + SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE); + return TRUE; + } + } + // Else we return TRUE, meaning we have cleared all actions + ClearAllActions(); + return TRUE; +} + +/************************ [AI_DetermineCombatRound] **************************** + This will do an action - EG: ActionAttack, against oIntruder, or against + the best (or random) target in range. + + At the end of combat, it heals up and searches for more enemies before + resuming normal activities. +************************* [History] ******************************************** + 1.0 - Started + 1.2 - Fixed minor bugs + 1.3 - Global targets added, cleaned up as Global* constants are used. +************************* [Workings] ******************************************* + It will start by checking the creatures effects, if they cannot move, nothing + is done. Daze, however, forces them to move away from last attacker. + + We then make sure we are not in an AOE spell, and not fleeing, if we are in + either, we react accordingly. + + After that, it sets up Global* targets, SpellTarget, RangedTarget, MeleeTarget, + and the integers such as GlobalAverageEnemyHD. + + If none are valid, it will search, but if we know there is an enemy around, + we move to them. + + If there is a valid target, at least one object Seen or Heard in our LOS, then + we perform a set of checks, and choose an action - normally using Class + abilities - such as Turn Undead, and Bard Songs, then spells, and finally + using Combat spells, and attacking the target using feats. Skills are also + used after spells. + + Dragons also run Wing Buffet, as well as using Breath attacks. +************************* [Arguments] ****************************************** + Arguments: oIntruder +************************* [AI_DetermineCombatRound] ***************************/ +void AI_DetermineCombatRound(object oIntruder = OBJECT_INVALID) +{ + // 46: "[DRC] START [Intruder]" + GetName(oIntruder) + DebugActionSpeakByInt(46, oIntruder); + + // Useful in the IsUncommandable check - we don't loop effects more than once. + AI_SetUpUsEffects(); + + // New check - If they are commandable, and no stupid ones. + if(AI_GetAIHaveEffect(GlobalEffectUncommandable) || + AI_GetAIHaveEffect(GlobalEffectParalyze) || + GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN) || + GetHasFeatEffect(FEAT_KNOCKDOWN) || + GetAIOff()) + { + DeleteAIObject(AI_LAST_MELEE_TARGET); + DeleteAIObject(AI_LAST_SPELL_TARGET); + DeleteAIObject(AI_LAST_RANGED_TARGET); + // 47: "[DCR] [PREMITURE EXIT] Cannot Do Anything." + DebugActionSpeakByInt(47); + return; + } + // 1.30 - daze is now as 3E rules, you can move around walking, but no + // attacking, casting or anything else :-( + else if(AI_GetAIHaveEffect(GlobalEffectDazed)) + { + // 48: "[DCR] [PREMITURE EXIT] Dazed move away." + DebugActionSpeakByInt(48); + // Equip best shield for most AC + AI_EquipBestShield(); + // Move away from the nearest heard enemy + GlobalNearestEnemyHeard = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + ActionMoveAwayFromObject(GlobalNearestEnemyHeard); + return; + } + // Tempory integer + int iTempInt; + + // Set combat AI level + iTempInt = GetAIConstant(LAG_AI_LEVEL_COMBAT); + if(iTempInt > iM1 && GetAILevel() != iTempInt) + { + SetAILevel(OBJECT_SELF, iTempInt); + } + // We stop - ClearAllActions normally + // NOTE: This returns FALSE if we don't stop actions - and want to carry on + // doing the thing before! like fleeing! (or we do it in the Stop thing). + if(!AI_StopWhatWeAreDoing()) + { + // 49: "[DCR] [PREMITURE EXIT] Fleeing or otherwise" + DebugActionSpeakByInt(49); + return; + } + + // Then we check all objects. we are going to perform a normal, or fleeing + // action this round, or action call. + + // Sets up us! + AI_SetUpUs(); + + // We set up 2 other targets...for testing against ETC. + GlobalNearestEnemySeen = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); + GlobalNearestEnemyHeard = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + // Valids? + GlobalValidNearestSeenEnemy = GetIsObjectValid(GlobalNearestEnemySeen); + GlobalValidNearestHeardEnemy = GetIsObjectValid(GlobalNearestEnemyHeard); + + // Speakstring arrays + if(GlobalValidNearestHeardEnemy) + { + // Add in range checking here + if(GetDistanceToObject(GlobalNearestEnemyHeard) < GetDistanceToObject(GlobalNearestEnemySeen)) + { + GlobalRangeToNearestEnemy = GetDistanceToObject(GlobalNearestEnemyHeard); + } + else + { + GlobalRangeToNearestEnemy = GetDistanceToObject(GlobalNearestEnemySeen); + } + + iTempInt = GetHitDice(GlobalNearestEnemyHeard); + // THEM_OVER_US - They have 5+ levels over us. + if(iTempInt - i5 >= GlobalOurHitDice) + { + SpeakArrayString(AI_TALK_ON_COMBAT_ROUND_THEM_OVER_US, TRUE); + } + // US_OVER_THEM - We have 5+ levels over them. + else if(GlobalOurHitDice - i5 >= iTempInt) + { + SpeakArrayString(AI_TALK_ON_COMBAT_ROUND_US_OVER_THEM, TRUE); + } + // EQUAL - Thier HD is within 4HD of us (EG: Us 10, them 10) + else //if(iTempInt - i4 <= GlobalOurHitDice && iTempInt + i4 >= GlobalOurHitDice) + { + SpeakArrayString(AI_TALK_ON_COMBAT_ROUND_EQUAL, TRUE); + } + } + else + { + GlobalRangeToNearestEnemy = f0; + } + // We may Dispel AOE's, move out of it, or ignore AOE's, EG Darkness. + // Dragons may use this (though small chance) + // Done before other things - best get out of some bad spells else they kill us! + // - Returns TRUE if we did anything that would mean we don't want to do + // another action + if(AI_AttemptSpecialChecks()){return;} + + // Sets up who to attack. + // - Uses oIntruder (to attack or move near) if anything. + // - We return TRUE if it ActionAttack's, or moves to an enemy - basically + // that we cannot do an action, but shouldn't search. False if normal. + // - We do this after AOE checking and special spells. + if(AI_SetUpAllObjects(oIntruder)){return;} + + // We then check if we have anyone to attack :-) This is a global integer. + if(GlobalAnyValidTargetObject) + { + // We do our auras. Quicken casted. + AI_ActionAbilityAura(); + + // We may flee from massive odds, or if we panic...or commoner fleeing + if(AI_AttemptMoraleFlee()){return;} + + // Beholder and Mindflayer special AI + iTempInt = GetAIInteger(AI_SPECIAL_AI); + if(iTempInt == i1) + { + // Beholder attacks. Should always return TRUE. Uses eye rays, + // casts spells, and teleports/flee's. + if(AI_AttemptBeholderCombat()){return;} + } + else if(iTempInt == i2) + { + // Special mindflayer things. This can fall through. + if(AI_AttemptMindflayerCombat()){return;} + } + + // We will attempt to heal ourselves first as a prioritory. + // Dragons may use this. + if(AI_AttemptHealingSelf()){return;} + // We will attempt to heal a previously set ally, with spells. + // Dragons use this, not always (and like to save spells for themselves). + if(AI_AttemptHealingAlly()){return;} + // We will cure, normally our conditions, or an allies. Things like + // blindness always, with other things later. + // Dragons use this, mostly with themselves. + if(AI_AttemptCureCondition()){return;} + + // This is a good thing to use first. Dragons may use this. + if(AI_AttemptFeatBardSong()){return;} + // We may summon our monster. Always first, because its our's and personal. Dragons may use this (though small chance) + if(AI_AttemptFeatSummonFamiliar()){return;} + // Turning, any sort of unturned and non-resistant creatures. + // Used about every 3 rounds. Dragons may use this. + if(AI_AttemptFeatTurning()){return;} + + // Special Dragon things now, else other monsters. + if(AI_GetIsDragon()) + { + // We may attempt a high level spells, wing buffet, and + // always attack with this. + if(AI_AttemptDragonCombat()){return;} + } + else + { + // We may move back as an archer, or even always do if set to. Normally + // only if we have help do we retreat out of AOO range. + // Will, if we can, cast invisibility first, or move back if we have it. + if(AI_AttemptArcherRetreat()){return;} + + // This may knockout dying PC's or coup de grace sleeping PC's nearby. + if(AI_AttemptGoForTheKill()){return;} + + // Spells attempt + if(AI_AttemptAllSpells()){return;} + + // We will attempt pickpocket/taunt/animal empathy after spells, before polymorph + if(AI_AttemptHostileSkills()){return;} + + // Attempt to cast all the buffing spells for melee - EG: Divine power, + // magical weapons, and ability enchanters. + if(AI_AttemptFeatCombatHostile()){return;} + + // We polymorph before attacking, if set so we can. + if(AI_AttemptPolyMorph()){return;} + + // This is called to attack, and should always attack something really. + if(AI_AttemptMeleeAttackWrapper()){return;} + } + } + // Else behaviour - we don't have a target. Any healing, extra or anything + // here. Then searching, and finally walking waypoints. + else + { + // We will attempt to heal ourselves first as a prioritory. + // Dragons may use this. + if(AI_AttemptHealingSelf()){return;} + // We will attempt to heal a previously set ally, with spells. + // Dragons use this, not always (and like to save spells for themselves). + if(AI_AttemptHealingAlly()){return;} + // We will cure, normally our conditions, or an allies. Things like + // blindness always, with other things later. + // Dragons use this, mostly with themselves. + if(AI_AttemptCureCondition()){return;} + } +// This is a call to the function which determines which way point to go back to. +// This will only run if it cannot heal self, is not in an AOE spell, and no target. + // 50: "[DRC] END - DELETE PAST TARGETS" + DebugActionSpeakByInt(50); + // Delete any last melee targets. + DeleteAIObject(AI_LAST_MELEE_TARGET); + DeleteAIObject(AI_LAST_SPELL_TARGET); + DeleteAIObject(AI_LAST_RANGED_TARGET); + DeleteLocalObject(OBJECT_SELF, "NW_GENERIC_LAST_ATTACK_TARGET"); + + // New: Search. This makes them go into search mode (if not already) and + // wanders around for an amount of time. We will search for AI_SEARCH_COOLDOWN_TIME + // seconds. + // - Search around oIntruder - might be a dead person. + Search(oIntruder); + // Search should activate this after a cirtain amount of time +} + + +// Debug: To compile this script full, uncomment all of the below. +/* - Add two "/"'s at the start of this line +void main() +{ + AI_DetermineCombatRound(); +} +//*/ diff --git a/_content/_hak/rune_prc8_top/j_inc_heartbeat.nss b/_content/_hak/rune_prc8_top/j_inc_heartbeat.nss new file mode 100644 index 0000000..0fe9771 --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_heartbeat.nss @@ -0,0 +1,277 @@ +/************************ [Heartbeat Include] ********************************** + Filename: J_Inc_Heartbeat +************************* [Heartbeat Include] ********************************** + This contains any heartbeat function calls. + + Note that the heartbeat uses ExecuteScript for larget behaviours that are + better split up so the heartbeat is as tiny as possible. +************************* [History] ******************************************** + 1.3 After Beta - Added +************************* [Workings] ******************************************* + This is included in nw_c2_default1 and j_ai_onheartbeat. + + Contains things like in j_inc_other_ai +************************* [Arguments] ****************************************** + Arguments: N/A +************************* [Heartbeat Include] *********************************/ + +#include "J_INC_CONSTANTS" + +// Bioware walk waypoints condition name +const string sWalkwayVarname = "NW_WALK_CONDITION"; +// Walk waypoint constant set in the SoU waypoint include +const int NW_WALK_FLAG_CONSTANT = 0x00000002; + +// Checks: +// * Dead +// * Uncommandable +// * No valid location +// * Petrified, paralised, ETC. +// Note: If sleep is found, it may apply Zzzz randomly, as well as stopping. +int JumpOutOfHeartBeat(); + +// This checks fleeing, door bashing and so on, to stop the heartbeat +// and perform the override special action, rather then run normal behaviour. +int PerformSpecialAction(); + +// Get whether the condition is set +// * Bioware SoU Waypoint call. +int GetWalkCondition(int nCondition, object oCreature=OBJECT_SELF); + +// Cast fleeing spells. +// - Invisiblity (best) +// - Haste/Expeditious Retreat +void ActionCastFleeingSpells(); + +// Attempt to cast iSpell. TRUE if true. +int FleeingSpellCast(int iSpell); + +int JumpOutOfHeartBeat() +{ + // What to return + int iReturn = FALSE; + // Checks: + // * Dead + Uncommandable are in GetAIOff + // * No valid location + // * Petrified, paralised, ETC. + // Note: If sleep is found, it may apply Zzzz randomly, as well as stopping. + + // Effect checking + effect eCheck = GetFirstEffect(OBJECT_SELF); + int iEffectType; + while(GetIsEffectValid(eCheck) && iReturn == FALSE) + { + iEffectType = GetEffectType(eCheck); + // Sleep is special + if(iEffectType == EFFECT_TYPE_SLEEP) + { + iReturn = i2;// This immediantly breaks. + } + // ALL these stop heartbeat. + else if(iEffectType == EFFECT_TYPE_PARALYZE || iEffectType == EFFECT_TYPE_STUNNED || + iEffectType == EFFECT_TYPE_FRIGHTENED || /* Removed sleep above */ + iEffectType == EFFECT_TYPE_TURNED || iEffectType == EFFECT_TYPE_PETRIFY || + iEffectType == EFFECT_TYPE_DAZED || iEffectType == EFFECT_TYPE_TIMESTOP || + iEffectType == EFFECT_TYPE_DISAPPEARAPPEAR || iEffectType == EFFECT_TYPE_CHARMED || + iEffectType == EFFECT_TYPE_DOMINATED || iEffectType == EFFECT_TYPE_CONFUSED) + { + iReturn = i1;// 1 = No Zzz. We continue to check for Zzz as well. + } + eCheck = GetNextEffect(OBJECT_SELF); + } + // Do we fire the heartbeat event? + if(iReturn != FALSE) + { + // If it is sleep... Zzzzz sometimes. + if(iReturn == i2 && d6() == i1) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, + EffectVisualEffect(VFX_IMP_SLEEP), + OBJECT_SELF); + } + FireUserEvent( + AI_FLAG_UDE_HEARTBEAT_EVENT, + EVENT_HEARTBEAT_EVENT);// Fire event 1001 + } + return iReturn; +} + +// This checks fleeing, door bashing and so on, to stop the heartbeat +// and perform the override special action, rather then run normal behaviour. +int PerformSpecialAction() +{ + int iAction = GetCurrentSetAction(); + object oTarget = GetAttackTarget(); + object oRunTarget; + switch(iAction) + { + // - Leader has made me a runner. I must run to a nearby group calling + // for help to get more men + case AI_SPECIAL_ACTIONS_ME_RUNNER: + { + oRunTarget = GetAIObject(AI_RUNNER_TARGET); + if(GetIsObjectValid(oRunTarget)) + { + if(GetObjectSeen(oRunTarget)) + { + // Stop thinking we are a runner if we can see the run target + ResetCurrentAction(); + AISpeakString(HELP_MY_FRIEND); + return FALSE; + } + else + { + // Else run to them + if(GetObjectHeard(oRunTarget)) + { + AISpeakString(HELP_MY_FRIEND); + } + ClearAllActions(); + ActionMoveToObject(oRunTarget, TRUE); + return TRUE; + } + } + } + break; + // - I am fleeing. + case AI_SPECIAL_ACTIONS_FLEE: + { + oRunTarget = GetAIObject(AI_FLEE_TO); + if(GetIsObjectValid(oRunTarget)) + { + // If they are a leader, and seen, and they are running, we + // obviously follow only. + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oRunTarget) || + GetLocalInt(oRunTarget, AI_CURRENT_ACTION) == AI_SPECIAL_ACTIONS_FLEE) + { + ClearAllActions(); + // New - cast fleeing spells. Important (and only used + // at higher intelligence) things like Expeditious retreat. + // - Only used once - one invisibility or haste. Deleted above. + ActionCastFleeingSpells(); + ActionForceFollowObject(oRunTarget, f3); + } + else if(GetObjectSeen(oRunTarget)) + { + // If we see the flee target, reset targets + ResetCurrentAction(); + // We will delete the local int (set to TRUE) which we + // stopped fleeing spells from + DeleteAIInteger(AI_HEARTBEAT_FLEE_SPELLS); + // Speak to allies to come :-) + AISpeakString(HELP_MY_FRIEND); + // Return FALSE. + return FALSE; + } + else + { + // Else flee! + if(GetObjectHeard(oRunTarget)) + { + AISpeakString(HELP_MY_FRIEND); + } + ClearAllActions(); + // New - cast fleeing spells. Important (and only used + // at higher intelligence) things like Expeditious retreat. + // - Only used once - one invisibility or haste. Deleted above. + ActionCastFleeingSpells(); + ActionMoveToObject(oRunTarget, TRUE); + return TRUE; + } + } + else + { + // Check if we have bad intellgence, and we will run away + // from the nearest enemy if heard. + if(GetAIInteger(AI_INTELLIGENCE) <= i3) + { + oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oRunTarget)) + { + oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oRunTarget)) + { + oRunTarget = GetLastHostileActor(); + if(!GetIsObjectValid(oRunTarget) || GetIsDead(oRunTarget)) + { + ResetCurrentAction(); + return FALSE; + } + } + } + // Run from enemy + ClearAllActions(); + ActionMoveAwayFromObject(oRunTarget, TRUE, f50); + return TRUE; + } + ResetCurrentAction(); + return FALSE; + } + } + break; + case AI_SPECIAL_ACTIONS_MOVE_OUT_OF_AOE: + { + // We must be X distance away from a cirtain AOE, if we are not, we + // move. + oRunTarget = GetAIObject(AI_AOE_FLEE_FROM); + + // If not valid, or already far enough away, delete special action + // and return false. + if(!GetIsObjectValid(oRunTarget) || + GetLocalFloat(OBJECT_SELF, AI_AOE_FLEE_FROM_RANGE) < GetDistanceToObject(oRunTarget)) + { + ResetCurrentAction(); + return FALSE; + } + else + { + // Valid and still in range + // - Run away + ClearAllActions(); + ActionMoveAwayFromLocation(GetLocation(oRunTarget), TRUE, GetLocalFloat(OBJECT_SELF, AI_AOE_FLEE_FROM_RANGE)); + return TRUE; + } + } + break; + } + // Return false to carry on a normal heartbeat + return FALSE; +} + +// Get whether the condition is set +// * Bioware SoU Waypoint call. +int GetWalkCondition(int nCondition, object oCreature=OBJECT_SELF) +{ + return (GetLocalInt(oCreature, sWalkwayVarname) & nCondition); +} + +// Cast fleeing spells. +// - Invisiblity (best) +// - Haste/Expeditious Retreat +void ActionCastFleeingSpells() +{ + // Not got local + if(GetAIInteger(AI_HEARTBEAT_FLEE_SPELLS)) return; + // Set local + SetAIInteger(AI_HEARTBEAT_FLEE_SPELLS, TRUE); + + // Invisibilities + if(FleeingSpellCast(SPELL_IMPROVED_INVISIBILITY)) return; + if(FleeingSpellCast(SPELL_INVISIBILITY)) return; + + // Haste + if(FleeingSpellCast(SPELL_MASS_HASTE)) return; + if(FleeingSpellCast(SPELL_HASTE)) return; + if(FleeingSpellCast(SPELL_EXPEDITIOUS_RETREAT)) return; +} + +// Attempt to cast iSpell. TRUE if true. +int FleeingSpellCast(int iSpell) +{ + if(GetHasSpell(iSpell)) + { + ActionCastSpellAtObject(iSpell, OBJECT_SELF); + return TRUE; + } + return FALSE; +} diff --git a/_content/_hak/rune_prc8_top/j_inc_npc_attack.nss b/_content/_hak/rune_prc8_top/j_inc_npc_attack.nss new file mode 100644 index 0000000..0946ff6 --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_npc_attack.nss @@ -0,0 +1,432 @@ +/************************ [Combat Attack] ************************************** + Filename: j_inc_npc_attack +************************* [Combat Attack] ************************************** + What does this do? + + It is a wrapper/include for getting a creature to attack target X, or do + Y. I use this for conversations, triggers, the lot, as a simple wrapper + that will execute my AI. + + There are several functions here to do things, that may be useful wrappers. + + And it also keeps Combat files SMALL! I uses Execute Script to fire the + combat file, not include it here. +************************* [History] ******************************************** + 1.3 - Added +************************* [Workings] ******************************************* + Include this in any conversation file or whatever, and mearly read the + descriptions of the different functions, and it will do what it says :-) +************************* [Arguments] ****************************************** + Arguments: +************************* [Combat Attack] *************************************/ + +// Include the constants for the combat, spawn integers ETC. +#include "j_inc_constants" + +// Hostile amount +const int HOSTILE = -100;// Reputation to change to +const int TYPE_ALL_PCS = 1;// is all PC's in the world. +const int TYPE_ALL_AREA = 2;// is all PC's in the specific area. +const int TYPE_IN_RANGE = 3;// is all PC's within fRange. + +// A slightly modified way to determine a combat round. +// * oTarget - The target to attack +// * sShout - The string to silently speak to get allies to come and help +void DetermineSpeakCombatRound(object oTarget = OBJECT_INVALID, string sShout = ""); + +// A slightly modified way to determine a combat round. +// * oTarget - The target to attack +// * oAttacker - The NPC who you want to determine a combat round, on oTarget +void DetermineSpeakCombatRoundNotMe(object oTarget, object oAttacker); + +// This is the main wrapper to get an NPC to attack in conversation. +// * fDelay - The delay AFTER adjusting reputation, that we attack and shout +// * iPlot - The plot flag to set to (Usually FALSE). +// * iImmortal - The immortal flag to set to (Usually FALSE). +// Example, how to keep flags already set: +// HostileAttackPCSpeaker(0.0, GetPlotFlag(), GetImmortal()); +// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't +void HostileAttackPCSpeaker(float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE); + +// This will make our faction hostile to the target, and attack them. +// * oTarget - The target object to attack +// * fDelay - The delay AFTER adjusting reputation, that we attack and shout +// * iPlot - The plot flag to set to (Usually FALSE). +// * iImmortal - The immortal flag to set to (Usually FALSE). +// Example, how to keep flags already set: +// HostileAttackObject(oPC, 0.0, GetPlotFlag(), GetImmortal()); +// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't +void HostileAttackObject(object oTarget, float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE); + +// This will make our faction hostile to the target, and shout. +// * oTarget - The target object to shout about. +// Use: Placeables, disturbers and so on. +// Note: Placeables are normally defaulted hostile faction! Must change it to work +void ShoutAbout(object oTarget); + +// This will make our faction hostile to ALL(!) PC's...in the area or game or range +// * iType - TYPE_ALL_PCS (1) is all PC's in the world. +// - TYPE_ALL_AREA (2) is all PC's in the specific area. +// - TYPE_IN_RANGE (3) is all PC's within fRange. +// * iPlot - The plot flag to set to (Usually FALSE). +// * iImmortal - The immortal flag to set to (Usually FALSE). +// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't +void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE); + +// This will thier most damaging weapon, and wait to disarm it. +// * fDuration - Delay until the weapon is withdrawn. +// * iRanged - if TRUE, it will equip a ranged weapon as a prioritory (EquipRanged call) +void EquipWeaponsDuration(float fDuration, int iRanged = FALSE); +// Disarms the persons right-hand-weapon +void RemoveWeapons(); + +// Plays talks like "ATTACK!" and "Group Near Me" etc. +// * iLowest, iHighest - the High/Lowest value to use. +// 0 = ATTACK, 1 = TAUNT, 2-4 = BATTLE(1-3), 5 = ENEMIES, 6 = GROUP, 7 = HELP. +void PlaySomeTaunt(int iLowest = 0, int iHighest = 7); + +// Gets all allies of ourselves to attack oTarget +// * oTarget - The target to attack. +void AlliesAttack(object oTarget); + +// Returns the nearest PC object +object GetNearestPCCreature(); +// Returns the nearest enemy (but doesn't determine if it can see/hear it) +object GetNearestEnemyCreature(); +// Returns the nearest friend +object GetNearestFriendCreature(); + + +// A slightly modified way to determine a combat round. +// * oTarget - The target to attack +// * sShout - The string to silently speak to get allies to come and help +void DetermineSpeakCombatRound(object oTarget, string sShout) +{ + // Shout + if(sShout != "") AISpeakString(sShout); + + // Check for custom AI script, else fire default. + string sAI = GetCustomAIFileName(); + // Fire default AI script + if(sAI == "") + { + // Sanity check - to not fire this off multiple times, we make sure temp + // object is not the same as oTarget (and valid) + if(!GetIsObjectValid(oTarget) || (GetIsObjectValid(oTarget) && + !GetLocalTimer(AI_DEFAULT_AI_COOLDOWN))) + { + SetLocalObject(OBJECT_SELF, AI_TEMP_SET_TARGET, oTarget); + ExecuteScript(COMBAT_FILE, OBJECT_SELF); + SetLocalTimer(AI_DEFAULT_AI_COOLDOWN, 0.1); + } + } + // Fire custom AI script + else + { + SetLocalObject(OBJECT_SELF, AI_TEMP_SET_TARGET, oTarget); + ExecuteScript(sAI, OBJECT_SELF); + } +} + +// A slightly modified way to determine a combat round. +// * oTarget - The target to attack +// * oAttacker - The NPC who you want to determine a combat round, on oTarget +void DetermineSpeakCombatRoundNotMe(object oTarget, object oAttacker) +{ + // Check for custom AI script, else fire default. + string sAI = GetLocalString(oAttacker, AI_CUSTOM_AI_SCRIPT); + // Fire default AI script + if(sAI == "") + { + // Sanity check - to not fire this off multiple times, we make sure temp + // object is not the same as oTarget (and valid) + if(!GetIsObjectValid(oTarget) || (GetIsObjectValid(oTarget) && + !GetLocalTimer(AI_DEFAULT_AI_COOLDOWN))) + { + SetLocalObject(oAttacker, AI_TEMP_SET_TARGET, oTarget); + ExecuteScript(COMBAT_FILE, oAttacker); + SetLocalTimer(AI_DEFAULT_AI_COOLDOWN, 0.1); + } + } + // Fire custom AI script + else + { + SetLocalObject(oAttacker, AI_TEMP_SET_TARGET, oTarget); + ExecuteScript(sAI, oAttacker); + } +} +// This is the main wrapper to get an NPC to attack in conversation. +// * iPlot - The plot flag to set to (Usually FALSE). +// * iImmortal - The immortal flag to set to (Usually FALSE). +// Example, how to keep flags already set: +// AttackPCSpeaker(GetPlotFlag(), GetImmortal()); +// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't +void HostileAttackPCSpeaker(float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE) +{ + // Get the PC + object oPC = GetPCSpeaker(); + // Error checking + if(!GetIsObjectValid(oPC) || GetIsDM(oPC)) return; + // Change the flags + if(GetPlotFlag() != iPlot) + SetPlotFlag(OBJECT_SELF, iPlot); + if(GetImmortal() != iImmortal) + SetImmortal(OBJECT_SELF, iPlot); + + // We make them hostile to our faction + AdjustReputation(oPC, OBJECT_SELF, HOSTILE); + // Attack them + SetLocalObject(OBJECT_SELF, AI_TO_ATTACK, oPC); + if(fDelay > 0.0) + { + // Round start... + DelayCommand(fDelay, DetermineSpeakCombatRound(oPC, I_WAS_ATTACKED)); + if(iAllAllies) + DelayCommand(fDelay, AlliesAttack(oPC)); + } + else + { + // Round start... + DetermineSpeakCombatRound(oPC, I_WAS_ATTACKED); + if(iAllAllies) AlliesAttack(oPC); + } +} + +// This will make our faction hostile to the target, and attack them. +// * oTarget - The target object to attack +// * iPlot - The plot flag to set to (Usually FALSE). +// * iImmortal - The immortal flag to set to (Usually FALSE). +// Example, how to keep flags already set: +// AttackObject(oPC, GetPlotFlag(), GetImmortal()); +// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't +void HostileAttackObject(object oTarget, float fDelay = 0.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE) +{ + // Error checking + if(!GetIsObjectValid(oTarget) || GetIsDM(oTarget)) return; + // Change the flags + if(GetPlotFlag() != iPlot) + SetPlotFlag(OBJECT_SELF, iPlot); + if(GetImmortal() != iImmortal) + SetImmortal(OBJECT_SELF, iPlot); + + // We make them hostile to our faction + AdjustReputation(oTarget, OBJECT_SELF, HOSTILE); + // Attack them + SetLocalObject(OBJECT_SELF, AI_TO_ATTACK, oTarget); + if(fDelay > 0.0) + { + // Round start... + DelayCommand(fDelay, DetermineSpeakCombatRound(oTarget, I_WAS_ATTACKED)); + } + else + { + // Round start... + DetermineSpeakCombatRound(oTarget, I_WAS_ATTACKED); + } +} + +// This will make our faction hostile to the target, and shout. +// * oTarget - The target object to shout about. +// Use: Placeables, disturbers and so on. +void ShoutAbout(object oTarget) +{ + // We make them hostile to our faction + AdjustReputation(oTarget, OBJECT_SELF, HOSTILE); + // And shout for others to attack + AISpeakString(CALL_TO_ARMS); +} + +// This will make our faction hostile to ALL(!) PC's...in the area or game or range +// * iType - TYPE_ALL_PCS (1) is all PC's in the world. +// - TYPE_ALL_AREA (2) is all PC's in the specific area. +// - TYPE_IN_RANGE (3) is all PC's within fRange. +// * iPlot - The plot flag to set to (Usually FALSE). +// * iImmortal - The immortal flag to set to (Usually FALSE). +// * iAllAllies - This will determine combat rounds against the target, that are in 50.0M. False = Don't +void HostileAttackAllPCs(int iType = 1, float fRange = 40.0, int iPlot = FALSE, int iImmortal = FALSE, int iAllAllies = TRUE) +{ + object oPC, oToAttack; + int iShout, iCnt; + float fNearestEnemy = 10000.0; + object oArea = GetArea(OBJECT_SELF); + switch(iType) + { + case TYPE_ALL_PCS:// s all PC's in the world. + { + oPC = GetFirstPC(); + while(GetIsObjectValid(oPC)) + { + if(!GetIsDM(oPC) && + GetIsObjectValid(GetArea(oPC))) + { + AdjustReputation(oPC, OBJECT_SELF, HOSTILE); + if(GetArea(oPC) == oArea) + { + if(GetDistanceToObject(oPC) <= fNearestEnemy) + { + oToAttack = oPC; + } + } + iShout = TRUE; + } + oPC = GetNextPC(); + } + } + break; + case TYPE_ALL_AREA:// is all PC's in the specific area. + { + iCnt = 1; + oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); + while(GetIsObjectValid(oPC)) + { + // Attack it! (if not a DM!) + if(!GetIsDM(oPC)) + { + AdjustReputation(oPC, OBJECT_SELF, HOSTILE); + if(GetArea(oPC) == oArea) + { + if(GetDistanceToObject(oPC) <= fNearestEnemy) + { + oToAttack = oPC; + } + } + iShout = TRUE; + } + // Next one + iCnt++; + oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, + OBJECT_SELF, iCnt); + } + } + break; + case TYPE_IN_RANGE:// is all PC's within fRange. + { + iCnt = 1; + oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); + while(GetIsObjectValid(oPC) && GetDistanceToObject(oPC) <= fRange) + { + // Attack it! (if not a DM!) + if(!GetIsDM(oPC)) + { + AdjustReputation(oPC, OBJECT_SELF, HOSTILE); + if(GetArea(oPC) == oArea) + { + if(GetDistanceToObject(oPC) <= fNearestEnemy) + { + oToAttack = oPC; + } + } + iShout = TRUE; + } + // Next one + iCnt++; + oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, + OBJECT_SELF, iCnt); + } + } + break; + } + // Attack nearest one (if valid) + if(GetIsObjectValid(oToAttack)) + { + DetermineSpeakCombatRound(oToAttack); + if(iAllAllies) AlliesAttack(oToAttack); + } + // Check if we shout + if(iShout) AISpeakString(CALL_TO_ARMS); +} +// This will thier most damaging melee weapon, and wait to disarm it. +// * fDuration - Delay until the weapon is withdrawn. +void EquipWeaponsDuration(float fDuration, int iRanged = FALSE) +{ + if(iRanged) + { + // Equip any most damaging (don't use oVersus, incase it doesn't arm anything) + ActionEquipMostDamagingRanged(); + } + else + { + // Equip any most damaging (don't use oVersus, incase it doesn't arm anything) + ActionEquipMostDamagingMelee(); + } + // Delay the un-equip + DelayCommand(fDuration, RemoveWeapons()); +} +// Disarms the persons right-hand-weapon +void RemoveWeapons() +{ + // cannot be in combat, duh! + if(GetIsInCombat() || GetIsObjectValid(GetAttackTarget())) + return; + // Get the weapon, make sure it is valid, and... + object oUnequip = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND); + if(GetIsObjectValid(oUnequip)) + { + // ...unequip it. + ClearAllActions(); + ActionUnequipItem(oUnequip); + } +} +/*:://///////////////////////////////////////////// +//:: PlaySomeTaunt +//:://///////////////////////////////////////////// + Plays talks like "ATTACK!" and "Group Near Me" etc. + * iLowest, iHighest - the High/Lowest value to use. + 0 = ATTACK, 1 = TAUNT, 2-4 = BATTLE(1-3), 5 = ENEMIES, 6 = GROUP, 7 = HELP. +//:://///////////////////////////////////////////// +//:: Created by : Jasperre +//:://///////////////////////////////////////////*/ +void PlaySomeTaunt(int iLowest, int iHighest) +{ + int iRandom = Random(iHighest) + iLowest; + int iVoice = VOICE_CHAT_ATTACK; + switch (iRandom) + { + case 0: iVoice = VOICE_CHAT_ATTACK; break; + case 1: iVoice = VOICE_CHAT_TAUNT; break; + case 2: iVoice = VOICE_CHAT_BATTLECRY1; break; + case 3: iVoice = VOICE_CHAT_BATTLECRY2; break; + case 4: iVoice = VOICE_CHAT_BATTLECRY3; break; + case 5: iVoice = VOICE_CHAT_ENEMIES; break; + case 6: iVoice = VOICE_CHAT_GROUP; break; + case 7: iVoice = VOICE_CHAT_HELP; break; + default: iVoice = VOICE_CHAT_ATTACK; break; + } + PlayVoiceChat(iVoice); +} + +// Gets all allies of ourselves to attack oTarget +// * oTarget - The target to attack. +void AlliesAttack(object oTarget) +{ + if(!GetIsObjectValid(oTarget)) return; + int iCnt = 1; + object oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, iCnt, CREATURE_TYPE_IS_ALIVE, TRUE); + while(GetIsObjectValid(oAlly) && GetDistanceToObject(oAlly) <= 50.0) + { + // A slightly modified way to determine a combat round. + // * oTarget - The target to attack + // * oAttacker - The NPC who you want to determine a combat round, on oTarget + DetermineSpeakCombatRoundNotMe(oTarget, oAlly); + iCnt++; + oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, iCnt, CREATURE_TYPE_IS_ALIVE, TRUE); + } +} + +// Returns the nearest PC object +object GetNearestPCCreature() +{ + return GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); +} +// Returns the nearest enemy (but doesn't determine if it can see/hear it) +object GetNearestEnemyCreature() +{ + return GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); +} +// Returns the nearest friend +object GetNearestFriendCreature() +{ + return GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND); +} + +//void main(){} diff --git a/_content/_hak/rune_prc8_top/j_inc_other_ai.nss b/_content/_hak/rune_prc8_top/j_inc_other_ai.nss new file mode 100644 index 0000000..c8d59ba --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_other_ai.nss @@ -0,0 +1,511 @@ +/************************ [Include - Other AI Functions] *********************** + Filename: j_inc_other_ai +************************* [Include - Other AI Functions] *********************** + This contains fuctions and calls for these scripts: + nw_c2_default2 - Percieve + nw_c2_default3 - On Combat round End (For DetermineCombatRound() only) + nw_c2_default4 - Conversation (shout) + nw_c2_default5 - Phisical attacked + nw_c2_default6 - Damaged + nw_c2_default8 - Disturbed + nw_c2_defaultb - Spell cast at + Ones that don't use this use different/No includes. + + HOPEFULLY it will make them faster, if they don't run combat. + + They use Execute Script to initiate combat. (With the override ones + initiating the override version, the normal initiateing the normal). +************************* [History] ******************************************** + 1.3 - Added to speed up compilings and gather non-combat, or other workings + in one place. +************************* [Workings] ******************************************* + This is included, by #include "J_INC_OTHER_AI" in other AI files. + + They then use these functions in them scripts. +************************* [Arguments] ****************************************** + Arguments: N/A +************************* Include - Other AI Functions] ***********************/ + +// All constants. +#include "j_inc_constants" + +// Responds to it (like makinging the callers attacker thier target) +// Called in OnConversation, and thats it. Use "ShouterFriend" To stop repeated GetIsFriend calls. +void RespondToShout(object oShouter, int nShoutIndex); +// Gets the attacker or attakee of the target, which should be a friend +object GetIntruderFromShout(object oShouter); + +// Shouts, or really brings all people in 60.0M(by default) to the "shouter" +void ShoutBossShout(object oEnemy); +// Checks the target for a specific EFFECT_TYPE constant value +// Returns TRUE or FALSE. Used On Damaged for polymorph checking. +int GetHasEffect(int nEffectType, object oTarget = OBJECT_SELF); +// This sets a morale penalty, to the exsisting one, if there is one. +// It will reduce itself after fDuration (or if we die, ETC, it is deleted). +// It is deleted at the end of combat as well. +void SetMoralePenalty(int iPenalty, float fDuration = 0.0); +// Removes iPenalty amount if it can. +void RemoveMoralePenalty(int iPenalty); +// At 5+ intelligence, we fire off any dispells at oPlaceables location +void SearchDispells(object oPlaceable); + +// This MAY make us set a local timer to turn off hiding. +// Turn of hiding, a timer to activate Hiding in the main file. This is +// done in each of the events, with the opposition checking seen/heard. +void TurnOffHiding(object oIntruder); +// Used when we percieve a new enemy and are not in combat. Hides the creature +// appropriatly with spawn settings and ability. +// - At least it will clear all actions if it doesn't set hiding on +void HideOrClear(); + +// This MIGHT move to oEnemy +// - Checks special actions, such as fleeing, and may run instead! +void ActionMoveToEnemy(object oEnemy); + +// Returns TRUE if we have under 0 morale, set to flee. +// - They then run! (Badly) +int PerceptionFleeFrom(object oEnemy); + +/*:://///////////////////////////////////////////// +//:: Name: ShoutBossShout +//:://///////////////////////////////////////////// + This is used in the OnPercieve, and if we are set to, + we will "shout" and bring lots of allies a running +//:://///////////////////////////////////////////*/ +void ShoutBossShout(object oEnemy) +{ + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER)) + { + // Get the range (and default to 60.0 M) + float fRange = IntToFloat(GetBoundriedAIInteger(AI_BOSS_MONSTER_SHOUT_RANGE, i60, 370)); + // We loop through nearest not-seen, not-heard allies and get them + // to attack the person. + int Cnt = i1; + // Not seen, not heard... + object oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + OBJECT_SELF, Cnt, CREATURE_TYPE_IS_ALIVE, TRUE, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD); + // Get who thier target is. + object oThierTarget; + while(GetIsObjectValid(oAlly) && GetDistanceToObject(oAlly) <= fRange) + { + oThierTarget = GetLocalObject(oAlly, AI_TO_ATTACK); + // If they are not attacking the enemy, we assing them to attack. + if(oThierTarget != oEnemy) + { + // Can't be in combat. + if(!GetIsInCombat(oAlly)) + { + // Set them to move to this + SetLocalObject(oAlly, AI_TO_ATTACK, oEnemy); + // Make them attack the person + SetLocalObject(oAlly, AI_TEMP_SET_TARGET, oEnemy); + ExecuteScript(COMBAT_FILE, oAlly); + } + } + Cnt++; + oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + OBJECT_SELF, Cnt, CREATURE_TYPE_IS_ALIVE, TRUE, + CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD); + } + // Remove it :-) + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER); + } +} +// This MAY make us set a local timer to turn off hiding. +// Turn of hiding, a timer to activate Hiding in the main file. This is +// done in each of the events, with the opposition checking seen/heard. +void TurnOffHiding(object oIntruder) +{ + if(!GetLocalTimer(AI_TIMER_TURN_OFF_HIDE) && + // Are we actually seen/heard or is it just an AOE? + (GetObjectSeen(OBJECT_SELF, oIntruder) || + GetObjectHeard(OBJECT_SELF, oIntruder))) + { + SetLocalTimer(AI_TIMER_TURN_OFF_HIDE, f18); + } +} + +// Used when we percieve a new enemy and are not in combat. Hides the creature +// appropriatly with spawn settings and ability. +// - At least it will clear all actions if it doesn't set hiding on +void HideOrClear() +{ + // Spawn in conditions for it + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_HIDING, AI_OTHER_COMBAT_MASTER) && + GetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH) == FALSE) + { + // Need skill or force on + if((GetSkillRank(SKILL_HIDE) - i4 >= GetHitDice(OBJECT_SELF)) || + GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER)) + { + // Use hide + ClearAllActions(TRUE); + SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE); + // Stop + return; + } + } + // Else clear all actions normally. + ClearAllActions(); +} + +/*:://///////////////////////////////////////////// +//:: Respond To Shouts +//:: Copyright (c) 2001 Bioware Corp. +//:://///////////////////////////////////////////// + Useage: + +//NOTE ABOUT BLOCKERS + + int NW_GENERIC_SHOUT_BLOCKER = 2; + + It should be noted that the Generic Script for On Dialogue attempts to get a local + set on the shouter by itself. This object represents the LastOpenedBy object. It + is this object that becomes the oIntruder within this function. + +//NOTE ABOUT INTRUDERS + + These are the enemy that attacked the shouter. + +//NOTE ABOUT EVERYTHING ELSE + + I_WAS_ATTACKED = 1; + + If not in combat, attack the attackee of the shouter. Basically the best + way to get people to come and help us. + + CALL_TO_ARMS = 3; + + If not in combat, determine combat round. By default, it should check any + allies it can see/hear for thier targets and help them too. + + HELP_MY_FRIEND = 4; + + This is a runner thing. Said when the runner sees the target to run to. + Gets a local location, and sets off people to run to it. + If no valid area for the location, no moving :-P + + We also shout this if we are fleeing. It will set the person to buff too. + + LEADER_FLEE_NOW = 5 + + We flee to a pre-set object or follow the leader (who should be fleeing). + + LEADER_ATTACK_TARGET = 6 + + We attack the intruder next round, by setting it as a local object to + override other choices. + + I_WAS_KILLED = 7 + + If lots are killed in one go - ouch! morale penalty each time someone dies. + + I_WAS_OPENED = 8 + + Chests/Doors which say this get the AI onto the tails of those who opened it, OR + they get searched! :-) +//:://///////////////////////////////////////////// +// Modified almost completely: Jasperre +//:://///////////////////////////////////////////*/ +// Gets the attacker or attakee of the target, which should be a friend +object GetIntruderFromShout(object oShouter) +{ + object oIntruder = GetAttackTarget(oShouter); + if(!GetIsObjectValid(oIntruder) || + GetIgnoreNoFriend(oIntruder)) + { + oIntruder = GetLastHostileActor(oShouter); + if(GetIgnoreNoFriend(oIntruder)) + { + return OBJECT_INVALID; + } + } + return oIntruder; +} + +void RespondToShout(object oShouter, int nShoutIndex) +{ + object oIntruder; + // Ones we don't care about if we are in combat... + if(nShoutIndex == i6) // "Attack specific object" + { + // If a leader, we set it as a local object, nothing more + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter)) + { + oIntruder = GetLocalObject(oShouter, AI_ATTACK_SPECIFIC_OBJECT); + if(GetObjectSeen(oIntruder)) + { + // Set local object to use in next DetermineCombatRound. + // We do not interrupt current acition (EG: Life saving stoneskins!) to re-direct. + SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, oIntruder); + // 6 second delay. + SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6); + } + } + return; + } + else if(nShoutIndex == i5)// "leader flee now" + { + // If a leader, we set it as a local object, nothing more + if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter)) + { + oIntruder = GetLocalObject(oShouter, AI_FLEE_TO); + // RUN! If intruder set is over 5.0M or no valid intruder + ClearAllActions(); + // 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput) + DebugActionSpeakByInt(70, oShouter, nShoutIndex); + SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE); + SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f12); + if(GetIsObjectValid(oIntruder)) + { + SetAIObject(AI_FLEE_TO, oIntruder); + ActionMoveToObject(oIntruder); + } + else // Else, we will just follow our leader! + { + SetAIObject(AI_FLEE_TO, oShouter); + ActionForceFollowObject(oShouter, f3); + } + } + return; + } + // If the shout is number 8, it is "I was opened" and so can only be a + // placeable or door. + else if(nShoutIndex == i8)// "I was opened" + { + // We need somewhat complexe here - to get thier opener. + int nType = GetObjectType(oShouter); + // Check object type. If not a placeable nor door - stop script. + if(nType == OBJECT_TYPE_PLACEABLE || + nType == OBJECT_TYPE_DOOR) + { + // Now, we assign the placeable/door to set thier opener. + // - Need to check it works. + AssignCommand(oShouter, SetLocalObject(oShouter, PLACEABLE_LAST_OPENED_BY, GetLastOpenedBy())); + oIntruder = GetLocalObject(oShouter, PLACEABLE_LAST_OPENED_BY); + if(GetIsObjectValid(oIntruder)) + { + // Attack + ClearAllActions(); + DetermineCombatRound(oShouter); + } + } + } + // Else, we must not be in combat for the rest + else if(!CannotPerformCombatRound()) + { + // Call to arms requires nothing special + if(nShoutIndex == i3)// "Call to arms" + { + SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6); + // 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput) + DebugActionSpeakByInt(70, oShouter, nShoutIndex); + DetermineCombatRound(); + } + // Ones we can GetIntruderFromShout(oShouter); + if(nShoutIndex == i1 || // "I was attacked" + nShoutIndex == i4 || // "Help my friend" + nShoutIndex == i7) // "I was killed" + { + // Am not already fighting, and we don't ignore the intruder + oIntruder = GetIntruderFromShout(oShouter); + if(!GetIsObjectValid(oIntruder)) + { + return; + } + } + if(nShoutIndex == i1 || + nShoutIndex == i7) + { + // Morale penalty if they were killed + if(nShoutIndex == i7) + { + SetMoralePenalty((GetHitDice(oShouter)/i4), f18); + } + // Get intruder + // 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput) + DebugActionSpeakByInt(70, oShouter, nShoutIndex); + if(GetObjectSeen(oIntruder)) + { + // Stop, and attack, if we can see them! + ClearAllActions(); + SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f9); + DetermineCombatRound(oIntruder); + DelayCommand(f2, AISpeakString(I_WAS_ATTACKED)); + } + else // Else the enemy is not seen + { + // If I can see neither the shouter nor the enemy + // stop what I am doing, and move to the attacker. + // - 1.3 change. They move to the attackers location (IE directed by ally) + ClearAllActions(); + SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6); + // This will move to oIntruder if nothing else + DetermineCombatRound(oIntruder); + // Shout to other allies, after a second. + DelayCommand(f2, AISpeakString(HELP_MY_FRIEND)); + } + } + else if(nShoutIndex == i4)// "Help my friend" + { + // We move to where the runner/shouter wants us. + location lMoveTo = GetLocalLocation(oShouter, AI_HELP_MY_FRIEND_LOCATION); + // 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput) + DebugActionSpeakByInt(70, oShouter, nShoutIndex); + SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6); + if(GetIsObjectValid(GetAreaFromLocation(lMoveTo))) + { + ActionMoveToLocation(lMoveTo, TRUE); + ActionDoCommand(DetermineCombatRound()); + } + else + { + // If we do not know of the friend attacker, we will follow them + ClearAllActions(); + SetSpawnInCondition(AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY, AI_COMBAT_MASTER); + ActionForceFollowObject(oShouter, f3); + ActionDoCommand(DetermineCombatRound()); + } + } + } +} + +void SearchDispells(object oPlaceable) +{ + // No dispelling at low intelligence. + if(GetBoundriedAIInteger(AI_INTELLIGENCE) < i5) return; + location lPlace = GetLocation(oPlaceable); + // Move closer if not seen. + if(!GetObjectSeen(oPlaceable)) + { + // Move nearer - 6 M is out of the dispell range + ActionMoveToObject(oPlaceable, TRUE, f6); + } + // Dispell if we have any - at the location of oPlaceable. + if(GetHasSpell(SPELL_LESSER_DISPEL)) + { + ActionCastSpellAtLocation(SPELL_LESSER_DISPEL, lPlace); + } + else if(GetHasSpell(SPELL_DISPEL_MAGIC)) + { + ActionCastSpellAtLocation(SPELL_DISPEL_MAGIC, lPlace); + } + else if(GetHasSpell(SPELL_GREATER_DISPELLING)) + { + ActionCastSpellAtLocation(SPELL_GREATER_DISPELLING, lPlace); + } + else if(GetHasSpell(SPELL_MORDENKAINENS_DISJUNCTION)) + { + ActionCastSpellAtLocation(SPELL_MORDENKAINENS_DISJUNCTION, lPlace); + } +} + +// Get Has Effect +// Checks to see if the target has a given +// effect, usually from a spell. Really useful this is. +int GetHasEffect(int nEffectType, object oTarget = OBJECT_SELF) +{ + effect eCheck = GetFirstEffect(oTarget); + while(GetIsEffectValid(eCheck)) + { + if(GetEffectType(eCheck) == nEffectType) + { + return TRUE; + break; + } + eCheck = GetNextEffect(oTarget); + } + return FALSE; +} + +// This sets a morale penalty, to the exsisting one, if there is one. +// It will reduce itself (by the penalty) after fDuration (or if we die, ETC, it is deleted). +// It is deleted at the end of combat as well. +void SetMoralePenalty(int iPenalty, float fDuration = 0.0) +{ + int iOriginal = GetAIInteger(AI_MORALE_PENALTY); + int iNew = iOriginal + iPenalty; + SetAIInteger(AI_MORALE_PENALTY, iNew); + DelayCommand(fDuration, RemoveMoralePenalty(iPenalty)); +} +void RemoveMoralePenalty(int iPenalty) +{ + int iOriginal = GetAIInteger(AI_MORALE_PENALTY); + int iNew = iOriginal - iPenalty; + if(iNew > 0 && !GetIsDead(OBJECT_SELF)) + { + SetAIInteger(AI_MORALE_PENALTY, iNew); + } + else + { + DeleteAIInteger(AI_MORALE_PENALTY); + } +} + +// This MIGHT move to oEnemy +// - Checks special actions, such as fleeing, and may run instead! +void ActionMoveToEnemy(object oEnemy) +{ + // Make sure that we are not fleeing badly (-1 morale from all enemies) + if(GetIsEnemy(oEnemy)) + { + // -1 morale, flee + if(PerceptionFleeFrom(oEnemy)) return; + } + if(GetIsPerformingSpecialAction()) + { + // Stop if we have an action we don't want to override + return; + } + // End default is move to the enemy + ClearAllActions(); + ActionMoveToObject(oEnemy, TRUE); + // combat round to heal/search/whatever + if(!GetFactionEqual(oEnemy)) + { + ActionDoCommand(DetermineCombatRound(oEnemy)); + } +} + +// Returns TRUE if we have under 0 morale, set to flee. +// - They then run! (Badly) +int PerceptionFleeFrom(object oEnemy) +{ + object oRunTarget = oEnemy; + if(GetAIInteger(AI_INTELLIGENCE) < FALSE) + { + // Valid run from target + if(!GetIsObjectValid(oRunTarget)) + { + oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oRunTarget)) + { + oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE); + if(!GetIsObjectValid(oRunTarget)) + { + oRunTarget = GetLastHostileActor(); + if(!GetIsObjectValid(oRunTarget) || GetIsDead(oRunTarget)) + { + // Stop - nothing to flee from! + return FALSE; + } + } + } + } + // Run from enemy + ClearAllActions(); + ActionMoveAwayFromObject(oRunTarget, TRUE, f50); + return TRUE; + } + // 0 or more morale. + return FALSE; +} + +// Debug: To compile this script full, uncomment all of the below. +/* - Add two "/"'s at the start of this line +void main() +{ + return; +} +//*/ diff --git a/_content/_hak/rune_prc8_top/j_inc_seteffects.nss b/_content/_hak/rune_prc8_top/j_inc_seteffects.nss new file mode 100644 index 0000000..6b48775 --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_seteffects.nss @@ -0,0 +1,444 @@ +/************************ [Set Effects Include] ******************************** + Filename: +************************* [Set Effects] **************************************** + This can be executed on a PC or NPC, and sets what thier current effects + are - the hostile ones. + + It is executed on a PC every 6 seconds in combat, delayed by a timer, + by other NPCs who want to target them. + + It is meant to be more efficient then doing countless checks against other + NPCs and PCs for what effects they already have on them. +************************* [History] ******************************************** + 1.3 - Added +************************* [Workings] ******************************************* + ExecuteScript - might not work faster. If so, it is easy to add into the + generic AI and have oTarget to set to. + + It searches the code and sets 3 custom integers, but only once (so not + during the loop) +************************* [Arguments] ****************************************** + Arguments: N/A +************************* [Set Effects] ***************************************/ + +#include "J_INC_CONSTANTS" + +// List (use Global to not conflict with the nwscript.nss!) +const int GlobalEffectUncommandable = 0x00000001;// Stun. Sleep. Fear. Turning. +const int GlobalEffectSilenced = 0x00000002;// Eeek! +const int GlobalEffectSlowed = 0x00000004;// Stop with haste. +const int GlobalEffectUltravision = 0x00000008; +const int GlobalEffectSeeInvisible = 0x00000010; // NOT Inc. Trueseeing +const int GlobalEffectTrueSeeing = 0x00000020; +const int GlobalEffectTimestop = 0x00000040; +const int GlobalEffectInvisible = 0x00000080; +const int GlobalEffectDeaf = 0x00000100;// Ack! +const int GlobalEffectHaste = 0x00000200; +const int GlobalEffectPolymorph = 0x00000400;// Only attack +const int GlobalEffectBlindness = 0x00000800;// Oh no! +const int GlobalEffectDisease = 0x00001000; +const int GlobalEffectPoison = 0x00002000; +const int GlobalEffectCurse = 0x00004000; +const int GlobalEffectNegativeLevel = 0x00008000; +const int GlobalEffectEntangle = 0x00010000; +const int GlobalEffectMovementSpeedDecrease = 0x00020000; +const int GlobalEffectDarkness = 0x00040000;// Noo! Need ultravision! +const int GlobalEffectDazed = 0x00080000;// Special: 1.30 patch, we can move! +const int GlobalEffectEthereal = 0x00100000; +const int GlobalEffectPetrify = 0x00200000; +const int GlobalEffectParalyze = 0x00400000;// Divided from Uncommandable for healing of +//const int GlobalEffectAbilityDecrease = 0x00080000;// Ohh! Tingly! +const int GlobalEffectSpellFailure = 0x00800000;// Makes sure spells are not cast under high failure. +const int GlobalEffectDamageShield = 0x01000000;// All damage shields +//int GlobalEffectAbilityDecrease = 0; // In combat include + +// These are Globals for spell effects, to not csat them on us again, and to +// speed things up... +const int GlobalHasStoneSkinProtections = 0x00000001; +const int GlobalHasElementalProtections = 0x00000002; +const int GlobalHasVisageProtections = 0x00000004; +const int GlobalHasMantalProtections = 0x00000008; +const int GlobalHasGlobeProtections = 0x00000010; +const int GlobalHasMindResistanceProtections = 0x00000020; +const int GlobalHasAidingSpell = 0x00000040; +const int GlobalHasRangedConsealment = 0x00000080; +const int GlobalHasRageSpells = 0x00000100; +const int GlobalHasBullsStrengthSpell = 0x00000200; +const int GlobalHasCatsGraceSpell = 0x00000400; +const int GlobalHasClairaudienceSpell = 0x00000800; +const int GlobalHasDeathWardSpell = 0x00001000; +const int GlobalHasDivinePowerSpell = 0x00002000; +const int GlobalHasEaglesSpledorSpell = 0x00004000; +const int GlobalHasEnduranceSpell = 0x00008000; +const int GlobalHasFoxesCunningSpell = 0x00010000; +const int GlobalHasProtectionEvilSpell = 0x00020000; +const int GlobalHasProtectionGoodSpell = 0x00040000; +const int GlobalHasLightSpell = 0x00080000; +const int GlobalHasConsealmentSpells = 0x00100000;// Displacement +const int GlobalHasProtectionSpellsSpell = 0x00200000; +const int GlobalHasRegenerateSpell = 0x00400000; +const int GlobalHasOwlsWisdomSpell = 0x00800000; +const int GlobalHasSpellResistanceSpell = 0x01000000; +const int GlobalHasSpellWarCrySpell = 0x02000000; +//const int GlobalHasElementalShieldSpell = 0x04000000; +const int GlobalHasDomainSpells = 0x08000000; +const int GlobalHasDeflectionACSpell = 0x10000000; +const int GlobalHasNaturalACSpell = 0x20000000; +const int GlobalHasOtherACSpell = 0x40000000; +const int GlobalHasWeaponHelpSpell = 0x80000000; + +// Other, AOE ones +//const int GlobalHasAreaEffectDamaging = 0x04000000; +//const int GlobalHasAreaEffectImpeding = 0x08000000; + +int TempEffectHex, TempSpellHex; + +// Sets up an effects thing to that +void AI_SetWeHaveEffect(int iEffectHex); +// Sets we have spell iSpellHex's effects. +void AI_SetWeHaveSpellsEffect(int iSpellHex); +// Sets up effects on oTarget +void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF); + + +// Simple return TRUE if it matches hex. +// - Effects tested on oTarget +int AI_GetAIHaveEffect(int iEffectHex, object oTarget = OBJECT_SELF); +// Simple return TRUE if it matches hex. +// - Uses oTarget +int AI_GetAIHaveSpellsEffect(int iSpellHex, object oTarget = OBJECT_SELF); + +// Sets up an effects thing to that +void AI_SetWeHaveEffect(int iEffectHex) +{ + TempEffectHex = TempEffectHex | iEffectHex; +} +// Sets we have spell iSpellHex's effects. +void AI_SetWeHaveSpellsEffect(int iSpellHex) +{ + TempSpellHex = TempSpellHex | iSpellHex; +} + +// Simple return TRUE if it matches hex. +// - Effects tested on oTarget +int AI_GetAIHaveEffect(int iEffectHex, object oTarget = OBJECT_SELF) +{ + return (GetLocalInt(oTarget, AI_EFFECT_HEX) & iEffectHex); + +} +// Simple return TRUE if it matches hex. +// - Uses oTarget +int AI_GetAIHaveSpellsEffect(int iSpellHex, object oTarget = OBJECT_SELF) +{ + return (GetLocalInt(oTarget, AI_SPELL_HEX) & iSpellHex); +} + + +void AI_SetEffectsOnTarget(object oTarget = OBJECT_SELF) +{ + TempEffectHex = FALSE; + TempSpellHex = FALSE; + // Checks our effects once. + effect eCheck = GetFirstEffect(oTarget); + int iEffect, iEffectAbilityDecrease, iSpellID; + // EFFECTS: + // For ALL targets (that we will use), we set up effects on a system of Hexes. + // like spawn in things. Replaces GetHasSpellEffect, except genralising - + // IE we will NOT cast more than one of the stoneskin type things at once. + while(GetIsEffectValid(eCheck)) + { + iEffect = GetEffectType(eCheck); + switch(iEffect) + { + case EFFECT_TYPE_INVALIDEFFECT: + case EFFECT_TYPE_VISUALEFFECT: + // Don't check these for spell values. + break; + case EFFECT_TYPE_PARALYZE: + AI_SetWeHaveEffect(GlobalEffectParalyze); + break; + case EFFECT_TYPE_STUNNED: + case EFFECT_TYPE_FRIGHTENED: + case EFFECT_TYPE_SLEEP: + case EFFECT_TYPE_TURNED: + case EFFECT_TYPE_DISAPPEARAPPEAR:// Added for dragon flying + AI_SetWeHaveEffect(GlobalEffectUncommandable); + break; + case EFFECT_TYPE_DAZED: + AI_SetWeHaveEffect(GlobalEffectDazed); + break; + case EFFECT_TYPE_SILENCE: + AI_SetWeHaveEffect(GlobalEffectSilenced); + break; + case EFFECT_TYPE_SLOW: + AI_SetWeHaveEffect(GlobalEffectSlowed); + break; + case EFFECT_TYPE_ULTRAVISION: + AI_SetWeHaveEffect(GlobalEffectUltravision); + break; + case EFFECT_TYPE_SEEINVISIBLE: + AI_SetWeHaveEffect(GlobalEffectSeeInvisible); + break; + // Caused by Beholder things mainly, but this stops any spell being + // cast, not just, for example, arcane spells cast in armor. + case EFFECT_TYPE_SPELL_FAILURE: + AI_SetWeHaveEffect(GlobalEffectSpellFailure); + break; + // Penetrates darkness. + case EFFECT_TYPE_TRUESEEING: + AI_SetWeHaveEffect(GlobalEffectTrueSeeing); + break; + // Timestop - IE don't cast same spell twice. + case EFFECT_TYPE_TIMESTOP: + AI_SetWeHaveEffect(GlobalEffectTimestop); + break; + // Invisibility/Improved (although improved only uses normal in the spell) + // Sneak attack/whatever :-) + // - include the spell EFFECT_TYPE_ETHEREAL. + case EFFECT_TYPE_INVISIBILITY: + case EFFECT_TYPE_IMPROVEDINVISIBILITY: + AI_SetWeHaveEffect(GlobalEffectInvisible); + break; + // Deaf - spell failing of 20%, but still cast. + case EFFECT_TYPE_DEAF: + AI_SetWeHaveEffect(GlobalEffectDeaf); + break; + // Special invis. + case EFFECT_TYPE_ETHEREAL: + AI_SetWeHaveEffect(GlobalEffectEthereal); + break; + // Haste - so don't cast haste again and whatever. + case EFFECT_TYPE_HASTE: + AI_SetWeHaveEffect(GlobalEffectHaste); + break; + // Haste - so don't cast haste again and whatever. + case EFFECT_TYPE_POLYMORPH: + AI_SetWeHaveEffect(GlobalEffectPolymorph); + break; + // Blindness - oh no, can't see, only hear! + case EFFECT_TYPE_BLINDNESS: + AI_SetWeHaveEffect(GlobalEffectBlindness); + break; + // Damage shield = Elemental shield, wounding whispers, Death armor, mestals + // sheth and so on. + case EFFECT_TYPE_ELEMENTALSHIELD: + AI_SetWeHaveEffect(GlobalEffectDamageShield); + break; + // Things we may want to remove VIA cirtain spells, we set here - may as well. + // Same setting as any other. + // IF we can remove it (not confusion ETC of course) then we set it. + case EFFECT_TYPE_DISEASE: + AI_SetWeHaveEffect(GlobalEffectDisease); + break; + case EFFECT_TYPE_POISON: + AI_SetWeHaveEffect(GlobalEffectPoison); + break; + // SoU Petrify + case EFFECT_TYPE_PETRIFY: + AI_SetWeHaveEffect(GlobalEffectPetrify); + break; + case EFFECT_TYPE_CURSE: + AI_SetWeHaveEffect(GlobalEffectCurse); + break; + case EFFECT_TYPE_NEGATIVELEVEL: + AI_SetWeHaveEffect(GlobalEffectNegativeLevel); + break; + case EFFECT_TYPE_ABILITY_DECREASE: + case EFFECT_TYPE_AC_DECREASE: + case EFFECT_TYPE_ATTACK_DECREASE: + case EFFECT_TYPE_DAMAGE_DECREASE: + case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE: + case EFFECT_TYPE_SAVING_THROW_DECREASE: + case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE: + case EFFECT_TYPE_SKILL_DECREASE: + // Special - we add one to this, to determine when to use restoration + iEffectAbilityDecrease++; + break; + case EFFECT_TYPE_ENTANGLE: + AI_SetWeHaveEffect(GlobalEffectEntangle); + break; + case EFFECT_TYPE_MOVEMENT_SPEED_DECREASE: + AI_SetWeHaveEffect(GlobalEffectMovementSpeedDecrease); + break; + case EFFECT_TYPE_DARKNESS: + AI_SetWeHaveEffect(GlobalEffectDarkness); + break; + default: + { + // Check spells we have on...so we don't cast over them! + iSpellID = GetEffectSpellId(eCheck); + if(iSpellID != iM1) + { + switch(iSpellID) + { + // All weapon things are on one variable. We cast the best. + case SPELL_MAGIC_WEAPON: + case SPELL_BLESS_WEAPON: + case SPELL_FLAME_WEAPON: + case SPELL_GREATER_MAGIC_WEAPON: + case SPELL_BLACKSTAFF: + case SPELL_BLADE_THIRST: + AI_SetWeHaveSpellsEffect(GlobalHasWeaponHelpSpell); + break; + case SPELL_ENDURE_ELEMENTS: + case SPELL_ENERGY_BUFFER: + case SPELL_RESIST_ELEMENTS: + case SPELL_PROTECTION_FROM_ELEMENTS: + AI_SetWeHaveSpellsEffect(GlobalHasElementalProtections); + break; + case SPELL_BARKSKIN: + case SPELL_STONE_BONES: // +3 to undead + AI_SetWeHaveSpellsEffect(GlobalHasNaturalACSpell); + break; + case SPELL_SHIELD: + case SPELL_DIVINE_SHIELD: + AI_SetWeHaveSpellsEffect(GlobalHasDeflectionACSpell); + break; + case SPELL_EPIC_MAGE_ARMOR:// Epic spell +20 to AC. + case SPELL_MAGE_ARMOR: + AI_SetWeHaveSpellsEffect(GlobalHasOtherACSpell); + break; + case SPELL_ENTROPIC_SHIELD: + AI_SetWeHaveSpellsEffect(GlobalHasRangedConsealment); + break; + case AI_SPELL_EPIC_WARDING: + case SPELL_STONESKIN: + case SPELL_GREATER_STONESKIN: + case SPELL_PREMONITION: + case SPELL_SHADES_STONESKIN: // Stoneskin one: 342 + AI_SetWeHaveSpellsEffect(GlobalHasStoneSkinProtections); + break; + case SPELL_GHOSTLY_VISAGE: + case SPELL_SHADOW_SHIELD: + case SPELL_ETHEREAL_VISAGE: + case SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE: // one: 351 is gostly visage. Speeds up not using number + case SPELL_SHADOW_EVADE: // Shadow dancer + case SPELLABILITY_AS_GHOSTLY_VISAGE: // Assassin + AI_SetWeHaveSpellsEffect(GlobalHasVisageProtections); + break; + case SPELL_GREATER_SPELL_MANTLE: + case SPELL_SPELL_MANTLE: + case SPELL_LESSER_SPELL_MANTLE: + AI_SetWeHaveSpellsEffect(GlobalHasMantalProtections); + break; + case SPELL_MINOR_GLOBE_OF_INVULNERABILITY: + case SPELL_GLOBE_OF_INVULNERABILITY: + case SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE: + AI_SetWeHaveSpellsEffect(GlobalHasGlobeProtections); + break; + case SPELL_AID: + case SPELL_PRAYER: + case SPELL_BLESS: + AI_SetWeHaveSpellsEffect(GlobalHasAidingSpell); + break; + case SPELL_BULLS_STRENGTH: + case SPELL_GREATER_BULLS_STRENGTH: + case SPELLABILITY_BG_BULLS_STRENGTH: // Blackguard + AI_SetWeHaveSpellsEffect(GlobalHasBullsStrengthSpell); + break; + case SPELL_CATS_GRACE: + case SPELL_GREATER_CATS_GRACE: + case AI_SPELL_HARPER_CATS_GRACE: // Harper + AI_SetWeHaveSpellsEffect(GlobalHasCatsGraceSpell); + break; + case SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE: + AI_SetWeHaveSpellsEffect(GlobalHasClairaudienceSpell); + break; + case SPELL_CLARITY: + case SPELL_LESSER_MIND_BLANK: + case SPELL_MIND_BLANK: + AI_SetWeHaveSpellsEffect(GlobalHasMindResistanceProtections); + break; + case SPELL_DEATH_WARD: + case SPELL_UNDEATHS_ETERNAL_FOE:// Similar to death ward. Got more things. + AI_SetWeHaveSpellsEffect(GlobalHasDeathWardSpell); + break; + case SPELL_DISPLACEMENT:// 50% consealment + AI_SetWeHaveSpellsEffect(GlobalHasConsealmentSpells); + break; + case SPELL_DIVINE_POWER: + AI_SetWeHaveSpellsEffect(GlobalHasDivinePowerSpell); + break; + case SPELL_EAGLE_SPLEDOR: + case SPELL_GREATER_EAGLE_SPLENDOR: + case AI_SPELL_HARPER_EAGLE_SPLEDOR: // Harper + AI_SetWeHaveSpellsEffect(GlobalHasEaglesSpledorSpell); + break; + case SPELL_ENDURANCE: + case SPELL_GREATER_ENDURANCE: + AI_SetWeHaveSpellsEffect(GlobalHasEnduranceSpell); + break; + case SPELL_FOXS_CUNNING: + case SPELL_GREATER_FOXS_CUNNING: + AI_SetWeHaveSpellsEffect(GlobalHasFoxesCunningSpell); + break; + case SPELL_HOLY_AURA: + case SPELL_MAGIC_CIRCLE_AGAINST_EVIL: + case SPELL_PROTECTION_FROM_EVIL: + AI_SetWeHaveSpellsEffect(GlobalHasProtectionEvilSpell); + break; + case SPELL_UNHOLY_AURA: + case SPELL_MAGIC_CIRCLE_AGAINST_GOOD: + case SPELL_PROTECTION_FROM_GOOD: + AI_SetWeHaveSpellsEffect(GlobalHasProtectionGoodSpell); + break; + case SPELL_LIGHT: + case SPELL_CONTINUAL_FLAME: + AI_SetWeHaveSpellsEffect(GlobalHasLightSpell); + break; + case SPELL_OWLS_WISDOM: + case SPELL_GREATER_OWLS_WISDOM: + case AI_SPELL_OWLS_INSIGHT:// Missed spell + AI_SetWeHaveSpellsEffect(GlobalHasOwlsWisdomSpell); + break; + case SPELL_PROTECTION_FROM_SPELLS: + AI_SetWeHaveSpellsEffect(GlobalHasProtectionSpellsSpell); + break; + case SPELL_REGENERATE: + AI_SetWeHaveSpellsEffect(GlobalHasRegenerateSpell); + break; + case SPELL_SPELL_RESISTANCE: + AI_SetWeHaveSpellsEffect(GlobalHasSpellResistanceSpell); + break; + case SPELL_WAR_CRY: + AI_SetWeHaveSpellsEffect(GlobalHasSpellWarCrySpell); + break; + case SPELLABILITY_DIVINE_PROTECTION: + case SPELLABILITY_DIVINE_STRENGTH: + case SPELLABILITY_DIVINE_TRICKERY: + case SPELLABILITY_BATTLE_MASTERY: + AI_SetWeHaveSpellsEffect(GlobalHasDomainSpells); + break; + case SPELLABILITY_RAGE_3: + case SPELLABILITY_RAGE_4: + case SPELLABILITY_RAGE_5: + case SPELLABILITY_BARBARIAN_RAGE: + case SPELLABILITY_INTENSITY_1: + case SPELLABILITY_INTENSITY_2: + case SPELLABILITY_INTENSITY_3: + case SPELLABILITY_FEROCITY_1: + case SPELLABILITY_FEROCITY_2: + case SPELLABILITY_FEROCITY_3: + case SPELL_BLOOD_FRENZY: + AI_SetWeHaveSpellsEffect(GlobalHasRageSpells); + break; + } + } + } + break; + } + eCheck = GetNextEffect(oTarget); + } + // If undead, set some immunities by default + if(GetRacialType(oTarget) == RACIAL_TYPE_UNDEAD) + { + AI_SetWeHaveSpellsEffect(GlobalHasDeathWardSpell); + } + DeleteLocalInt(oTarget, AI_ABILITY_DECREASE); + DeleteLocalInt(oTarget, AI_EFFECT_HEX); + DeleteLocalInt(oTarget, AI_SPELL_HEX); + // Set final ones from temp integers + SetLocalInt(oTarget, AI_ABILITY_DECREASE, iEffectAbilityDecrease); + SetLocalInt(oTarget, AI_EFFECT_HEX, TempEffectHex); + SetLocalInt(oTarget, AI_SPELL_HEX, TempSpellHex); +} diff --git a/_content/_hak/rune_prc8_top/j_inc_setweapons.nss b/_content/_hak/rune_prc8_top/j_inc_setweapons.nss new file mode 100644 index 0000000..a8f708f --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_setweapons.nss @@ -0,0 +1,2043 @@ +/************************ [Include - Set Weapons] ****************************** + Filename: J_Inc_Setweapons +************************* [Include - Set Weapons] ****************************** + This holds all the stuff for setting up local objects for weapons we have + or have not got - normally the best to worst. +************************* [History] ******************************************** + 1.0 - Put in include + 1.3 - Fixed minor things, added arrays of weapons (for deul wielding) and + heal kits and stuff added. + Added to OnSpawn. +************************* [Workings] ******************************************* + This is included in "j_ai_setweapons" and executed from other places VIA it. + + This migth change, but not likely. It could be re-included OnSpawn at least + for spawning in. + + Yraen's script really. Modified some things in it though, trying to speed it up + Changed things like if statements to switches, tried (somewhat unsucessfully) to + Add the magical staff (never equips it?) and the values are now much cleaner and + better valued. + + NOTE: + + - Ignores OVERWHELMING CRITICAL + - Ignores DEVASTATING CRITICAL + - Ignores EPIC WEAPON FOCUS + - Ignores EPIC WEAPON SPECIALIZATION + - Ignores WEAPON OF CHOICE + + So if you really want the item to be used, just use one weapon. + + This is because epic feats, 5 lots, xAmount of checks, is many too many for + it to be worth it. Maybe less then 1% would ever come up true, out of the + many GetHasFeat() checks. + + The lower level ones are taken into account, however! And these are much + more common. +************************* [Arguments] ****************************************** + Arguments: N/A +************************* [Include - Set Weapons] *****************************/ + +#include "j_inc_constants" + +/*****Structure******/ +// Things we use throughout, saves time (and Get's) putting them here. + +int ProfWizard, ProfDruid, ProfMonk, ProfElf, ProfRogue, ProfSimple, + ProfMartial, ProfExotic, ProfShield, +/* +int ProfWizard = FALSE; +int ProfDruid = FALSE; +int ProfMonk = FALSE; +int ProfElf = FALSE; +int ProfRogue = FALSE; +int ProfSimple = FALSE; +int ProfMartial = FALSE; +int ProfExotic = FALSE; +int ProfShield = FALSE; +*/ +// If we set have the right two-weapon fighting feats, this is set +/*int*/ProfTwoWeapons, // = FALSE; +// This contains our current size. +/*int*/CreatureSize, +/*int*/CreatureStrength, // = FALSE; +// This tracks the current item value (so less Get/Set). +/*int*/CurrentItemValue, // = FALSE; +/*int*/CurrentItemIsMighty, // = FALSE; +/*int*/CurrentItemIsUnlimited, // = FALSE; +/*int*/CurrentItemSize, // = FALSE; +/*int*/CurrentItemDamage, // = FALSE; // Special - Damage is set in the bigger arrays. +/*int*/CurrentItemType, // = -1; // Set to -1 in loop anyway. +// I'm scripting, not doing grammer here! +/*int*/HasArrows, // = FALSE; +/*int*/HasBolts, // = FALSE; +/*int*/HasBullets; // = FALSE; + +// String for setting a value integer to an item +const string SETWEP_VALUE = "VALUE"; +const string SETWEP_DISTANCE = "DISTANCE"; +const string SETWEP_IS_UNLIMITED = "SETWEP_IS_UNLIMITED"; +const string SETWEP_SHIELD = "SHIELD"; + +// Constants for weapon size. +const int WEAPON_SIZE_INVALID = 0; +const int WEAPON_SIZE_TINY = 1; +const int WEAPON_SIZE_SMALL = 2; +const int WEAPON_SIZE_MEDIUM = 3; +const int WEAPON_SIZE_LARGE = 4; + +//int CREATURE_SIZE_INVALID = 0; +//int CREATURE_SIZE_TINY = 1; +//int CREATURE_SIZE_SMALL = 2; +//int CREATURE_SIZE_MEDIUM = 3; +//int CREATURE_SIZE_LARGE = 4; +//int CREATURE_SIZE_HUGE = 5; + +/**** MAIN CALLS ****/ +// Start of the whole thing... +// - Runs through ALL inventory items, and the weapon/ammo slots, to +// set what weapons are best to use, backup ones, shields and so forth! +void SetWeapons(object oTarget = OBJECT_SELF); +// Goes through and sets a value and then a weopen to all weopen items +void SortInventory(object oTarget); +// This returns the size of oItem +int GetWeaponSize(object oItem = OBJECT_INVALID); +// reset healing kits only on oTarget. +void ResetHealingKits(object oTarget); + +/**** SETTING ****/ +// Ranged weopen is set - final one to use. +void SetRangedWeapon(object oTarget); +// Sets the primary weopen to use. +void SetPrimaryWeapon(object oTarget, object oItem = OBJECT_INVALID); +// sets the Two Handed Weopen to use. +void SetTwoHandedWeapon(object oTarget, object oItem = OBJECT_INVALID); +// Ammo counters are set, although I do not think they specifically are equipped. +void SetAmmoCounters(object oTarget); +// Sets the object shield to use. Best one. +void SetShield(object oTarget); +// Like the ranged weapons, we set this so we can have 2 shields (so we can tell +// when to re-set such things). +void StoreShield(object oTarget, object oItem); +// Uses right prefix to store the object to oTarget. +void SWFinalAIObject(object oTarget, string sName, object oObject); +// Uses right prefix to store the iInt to oTarget. +void SWFinalAIInteger(object oTarget, string sName, int iInt); +// Deletes object with Prefix +void SWDeleteAIObject(object oTarget, string sName); +// Deletes integer with Prefix +void SWDeleteAIInteger(object oTarget, string sName); + +// Sets the weapon to the array, in the right spot... +// If iSecondary is TRUE, it uses the weapon size, and creature size to modifiy +// the value. +void ArrayOfWeapons(string sArray, object oTarget, object oItem, int iValue, int iSecondary = FALSE); +// Deletes all the things in an array...set to sArray +void DeleteDatabase(object oTarget, string sArray); +// Deletes all things, before we start! +void DeleteAllPreviousWeapons(object oTarget); + +/**** STORING ****/ +// Stores the ranged weopen - it also needs to check ammo before choosing one. +void StoreRangedWeapon(object oTarget, object oItem = OBJECT_INVALID); +// This adds the maximum damage onto the value +void BaseLargeWeapons(object oTarget, object oItem = OBJECT_INVALID); +// This adds the maximum damage onto the value. +void BaseMediumWeapons(object oTarget, object oItem = OBJECT_INVALID); +// This adds the maximum damage onto the value +void BaseSmallWeapons(object oTarget, object oItem = OBJECT_INVALID); +// This adds the maximum damage onto the value +void BaseTinyWeapons(object oTarget, object oItem = OBJECT_INVALID); +// This adds the effects onto the value +void BaseEffects(object oTarget, object oItem = OBJECT_INVALID); +// This will take the weapon size, and things, and apply the right base effects. +void DoEffectsOf(object oTarget, object oItem); + +/*** OTHER ****/ +// Erm...deletes the ints. Like wizard and so on. +void DeleteInts(object oTarget); +// This returns _STATE local int +int GetState(object oTarget); +// This is the deletion of the values of weapons. +void DeleteValueInts(object oTarget, string sArray); +// This moves the values from iMax to iNumberStart back one in the list. +void MoveArrayBackOne(string sArray, int iNumberStart, object oTarget, int iMax); +// Special: Apply EffectCutsceneImmobilize +void AI_SpecialActionApplyItem(object oTarget); +// Special: Remove EffectCutsceneImmobilize +void AI_SpecialActionRemoveItem(object oTarget); +// Gets a item talent value, no applying of EffectCutsceneImmobilize. +// - iTalent, 1-21. +void AI_SetItemTalentValue(int iTalent); + +//:://///////////////////////////////////////////// +//:: Name SetWeopens +//::////////////////////////////////////////////// +/* + Main call - it starts the process of checking + Inventory, and so on +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void SetWeapons(object oTarget = OBJECT_SELF) +{ + // Deletes all previous things, even if we don't have them, just in case. + DeleteAllPreviousWeapons(oTarget); + + // We don't do this if we have AI_FLAG_OTHER_LAG_EQUIP_MOST_DAMAGING set. + if(GetSpawnInCondition(AI_FLAG_OTHER_LAG_EQUIP_MOST_DAMAGING, AI_OTHER_MASTER)) return; + + // Gets the creature size, stores it... + CreatureSize = GetCreatureSize(oTarget); + // No need to take off strength. It is pulrey for mighty weapons, we + // add on this bonus to the value. + CreatureStrength = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + if(CreatureStrength < i0) + { + CreatureStrength = i0; + } + // Ints, globally set. + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oTarget)) + ProfDruid = TRUE; + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oTarget)) + ProfElf = TRUE; + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oTarget)) + ProfExotic = TRUE; + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oTarget)) + ProfMartial = TRUE; + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oTarget)) + ProfMonk = TRUE; + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oTarget)) + ProfRogue = TRUE; + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oTarget)) + ProfSimple = TRUE; + if(GetHasFeat(FEAT_WEAPON_PROFICIENCY_WIZARD, oTarget)) + ProfWizard = TRUE; + if(GetHasFeat(FEAT_SHIELD_PROFICIENCY, oTarget)) + ProfShield = TRUE; + if(GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oTarget) || + GetHasFeat(FEAT_AMBIDEXTERITY, oTarget) || + GetHasFeat(FEAT_IMPROVED_TWO_WEAPON_FIGHTING, oTarget)) + { + ProfTwoWeapons = TRUE; + } + // Sorts the inventory, on oTarget, with CreatureSize of creature + SortInventory(oTarget); +} + +//:://///////////////////////////////////////////// +//:: Name SortInventory +//::////////////////////////////////////////////// +/* + Right - Goes through all items in the inventory + It, based in Weopen size and creature size, + do base effects of it (value it), if a weopen +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void SortInventory(object oTarget) +{ + // Note to self: Removed potion setting. THis is done each round in AI include + // because it is probably better that way. + int nBase, nWeaponSize, iCnt; + object oItem, oHighestKit; + int iHealingKitsAmount, iItemValue; + int iRunningValue = i0; // For kits + + // Onto the slots - if we are checking them! + // Slots 4 and 5. (HTH weapons) + for(iCnt = INVENTORY_SLOT_RIGHTHAND; // 4 + iCnt <= INVENTORY_SLOT_LEFTHAND; // 5 + iCnt++) + { + oItem = GetItemInSlot(iCnt, oTarget); + if(GetIsObjectValid(oItem)) + { + CurrentItemType = GetBaseItemType(oItem); + CurrentItemSize = GetWeaponSize(oItem); + CurrentItemDamage = FALSE;// Reset + if(CurrentItemSize)// Is over 0 + { + DoEffectsOf(oTarget, oItem); + } + } + } + // Slots 11, 12 and 13. (some ammo slots) + for(iCnt = INVENTORY_SLOT_ARROWS; //11 + iCnt <= INVENTORY_SLOT_BOLTS; //13 + iCnt++) + { + oItem = GetItemInSlot(iCnt, oTarget); + if(GetIsObjectValid(oItem)) + { + CurrentItemType = GetBaseItemType(oItem); + CurrentItemSize = GetWeaponSize(oItem); + CurrentItemDamage = FALSE;// Reset + if(CurrentItemSize) + { + DoEffectsOf(oTarget, oItem); + } + } + } + // The inventory + oItem = GetFirstItemInInventory(oTarget); + while(GetIsObjectValid(oItem)) + { + // Added some else statements to speed it up + CurrentItemType = GetBaseItemType(oItem); + if(CurrentItemType == BASE_ITEM_HEALERSKIT) + { + iHealingKitsAmount++; + iItemValue = GetGoldPieceValue(oItem); + // Stacked kits be worth what they should be seperatly. + iItemValue = iItemValue/GetNumStackedItems(oItem); + if(iItemValue > iRunningValue) + { + iRunningValue = iItemValue; + oHighestKit = oItem; + } + } + // Else, is it a arrow, bolt or bullet? + else if(CurrentItemType == BASE_ITEM_ARROW || + CurrentItemType == BASE_ITEM_BOLT || + CurrentItemType == BASE_ITEM_BULLET) + { + SetAmmoCounters(oTarget); + } + else + // else it isn't a healing kit, or ammo...what is it? + // Likely a weapon, so we check + { + // Only need current item size, if it is a weapon! + CurrentItemSize = GetWeaponSize(oItem); + CurrentItemDamage = FALSE;// Reset + if(CurrentItemSize)// Is over 0 (valid weapon) + { + // Do the appropriate enchantment issuse and so on. + DoEffectsOf(oTarget, oItem); + } + } + oItem = GetNextItemInInventory(oTarget); + } + // Set our ranged weapons (if any) + SetRangedWeapon(oTarget); + // Set our shield (if any) + SetShield(oTarget); + // Need some, any! + if(iHealingKitsAmount > i0) + { + // set healing kits (if any) + SWFinalAIObject(oTarget, AI_VALID_HEALING_KIT_OBJECT, oHighestKit); + // Set amount left + SWFinalAIInteger(oTarget, AI_VALID_HEALING_KITS, iHealingKitsAmount); + } + // Added in item setting functions. apply EffectCutsceneImmobilize, remove at end, but + // in the middle we do item talents. + if(!GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_ITEMS, AI_OTHER_MASTER, oTarget)) + { + AI_SpecialActionApplyItem(oTarget); + + // Loop talents (not ones we won't set however) + for(iCnt = i1; iCnt <= i15; iCnt++) + { + // Ignore healing ones. + if(iCnt != i4 && iCnt != i5) + { + AI_SetItemTalentValue(iCnt); + } + } + + AI_SpecialActionRemoveItem(oTarget); + } + // Delete things, FINALLY. really! I mean, this is it, it runs, as it is, + // and the other things run off it as things are met...! + DelayCommand(0.1, DeleteInts(oTarget)); +} + +void DoEffectsOf(object oTarget, object oItem) +{ + // 1.3 = changed to switch statement. + // Note: Anything not done BaseEffects of cannot even be used by the character. + switch(CurrentItemSize) + { + // Tiny weapons - If we are under large size, and is a dagger or similar + case WEAPON_SIZE_TINY: + { + if(CreatureSize < CREATURE_SIZE_LARGE) BaseEffects(oTarget, oItem); + } + break; + // Small Weapons - If we are large (not giant) and size is like a shortsword + case CREATURE_SIZE_SMALL: + { + if(CreatureSize < CREATURE_SIZE_HUGE) BaseEffects(oTarget, oItem); + } + break; + // Medium weapons - If we are over tiny, and size is like a longsword + case WEAPON_SIZE_MEDIUM: + { + if(CreatureSize > CREATURE_SIZE_TINY) BaseEffects(oTarget, oItem); + } + break; + // Large weapons - anything that is over small, and the size is like a spear + case WEAPON_SIZE_LARGE: + { + if(CreatureSize > WEAPON_SIZE_SMALL) BaseEffects(oTarget, oItem); + } + break; + } +} + +//:://///////////////////////////////////////////// +//:: Name BaseEffects +//::////////////////////////////////////////////// +/* + Sets the value (+/- int) of the item + Things like haste are worth more... +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void BaseEffects(object oTarget, object oItem) +{ + // Reset value + CurrentItemValue = i0; + if(GetIsObjectValid(oItem)) + { + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ABILITY_BONUS)) + CurrentItemValue += i8; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_AC_BONUS)) + CurrentItemValue += i5; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_AC_BONUS_VS_ALIGNMENT_GROUP)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_AC_BONUS_VS_DAMAGE_TYPE)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_AC_BONUS_VS_RACIAL_GROUP)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_AC_BONUS_VS_SPECIFIC_ALIGNMENT)) + CurrentItemValue += i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ATTACK_BONUS)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP)) + CurrentItemValue += i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP)) + CurrentItemValue += i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT)) + CurrentItemValue += i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_BASE_ITEM_WEIGHT_REDUCTION)) + CurrentItemValue += i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_BONUS_FEAT)) + CurrentItemValue += i6; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_BONUS_SPELL_SLOT_OF_LEVEL_N)) + CurrentItemValue += i2; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_CAST_SPELL)) + CurrentItemValue += i5; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DAMAGE_BONUS)) + CurrentItemValue += i6; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DAMAGE_REDUCTION)) + CurrentItemValue += i8; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DAMAGE_RESISTANCE)) + CurrentItemValue += i8; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DAMAGE_VULNERABILITY)) + CurrentItemValue -= i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DARKVISION)) + CurrentItemValue += i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_ABILITY_SCORE)) + CurrentItemValue -= i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_AC)) + CurrentItemValue -= i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER)) + CurrentItemValue -= i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_DAMAGE)) + CurrentItemValue -= i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER)) + CurrentItemValue -= i5; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_SAVING_THROWS)) + CurrentItemValue -= i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_SAVING_THROWS_SPECIFIC)) + CurrentItemValue -= i3; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_DECREASED_SKILL_MODIFIER)) + CurrentItemValue -= i2; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS)) + CurrentItemValue += i7; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP)) + CurrentItemValue += i6; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP)) + CurrentItemValue += i6; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT)) + CurrentItemValue += i5; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_EXTRA_MELEE_DAMAGE_TYPE)) + CurrentItemValue += i1; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_EXTRA_RANGED_DAMAGE_TYPE)) + CurrentItemValue += i1; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_FREEDOM_OF_MOVEMENT)) + CurrentItemValue += i5; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_HASTE)) + CurrentItemValue += i12; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_HOLY_AVENGER)) + CurrentItemValue += i10; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE)) + CurrentItemValue += i8; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS)) + CurrentItemValue += i10; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL)) + CurrentItemValue += i8; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_IMMUNITY_SPELL_SCHOOL)) + CurrentItemValue += i12; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_IMPROVED_EVASION)) + CurrentItemValue += i10; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_KEEN)) + CurrentItemValue += i7; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_LIGHT)) + CurrentItemValue += i1; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_MASSIVE_CRITICALS)) + CurrentItemValue += i2; +// if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_MIND_BLANK)) +// CurrentItemValue += i4;// Do not think It exsists. + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_MONSTER_DAMAGE)) + CurrentItemValue += i1; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_NO_DAMAGE)) + CurrentItemValue -= i10;// EEEKK! Bad bad bad!! + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ON_HIT_PROPERTIES)) + CurrentItemValue += i8;// Includes all vorpal and so on! +// if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_ON_MONSTER_HIT)) +// CurrentItemValue += i8;// Can't be on a weapon + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_POISON)) + CurrentItemValue += i5; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_REGENERATION)) + CurrentItemValue += i8; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_REGENERATION_VAMPIRIC)) + CurrentItemValue += i6; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_SAVING_THROW_BONUS)) + CurrentItemValue += i5; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC)) + CurrentItemValue += i4; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_SKILL_BONUS)) + CurrentItemValue += i2; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_SPELL_RESISTANCE)) + CurrentItemValue += i7; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_TRUE_SEEING)) + CurrentItemValue += i11; + if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_TURN_RESISTANCE)) + CurrentItemValue += i8; +// if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_VORPAL)) +// CurrentItemValue += i8;// Removed as Bioware will remove this constant. Doesn't exsist. +// if(GetItemHasItemProperty(oItem, ITEM_PROPERTY_WOUNDING)) +// CurrentItemValue += i8;// Removed as Bioware will remove this constant. Doesn't exsist. + // Special cases + // Set is unlimited to TRUE or FALSE, add 10 if TRUE. + CurrentItemIsUnlimited = GetItemHasItemProperty(oItem, ITEM_PROPERTY_UNLIMITED_AMMUNITION); + if(CurrentItemIsUnlimited) CurrentItemValue += i10; + // Same as above, for mighty + CurrentItemIsMighty = GetItemHasItemProperty(oItem, ITEM_PROPERTY_MIGHTY); + if(CurrentItemIsMighty) CurrentItemValue += i3; + + switch (CurrentItemSize) + { + case WEAPON_SIZE_INVALID:// Invalid Size, stop + { + return; + } + break; + case WEAPON_SIZE_TINY:// Tiny weapons (EG: Daggers, Slings) + { + BaseTinyWeapons(oTarget, oItem); + return; + } + break; + case WEAPON_SIZE_SMALL:// Small Weapons (EG short Swords) + { + BaseSmallWeapons(oTarget, oItem); + return; + } + break; + case WEAPON_SIZE_MEDIUM: // Medium weapons (EG long swords) + { + BaseMediumWeapons(oTarget, oItem); + return; + } + break; + case WEAPON_SIZE_LARGE: // Large Weapons (EG greataxes) + { + BaseLargeWeapons(oTarget, oItem); + return; + } + break; + } + } +} + +//:://///////////////////////////////////////////// +//:: Name BaseLargeWeapons +//::////////////////////////////////////////////// +/* + This adds the maximum damage onto the value +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void BaseLargeWeapons(object oTarget, object oItem) +{ + // No need for weopen size...we know we can use it! + switch(CurrentItemType) + { + case BASE_ITEM_DIREMACE: + { + // This is the only one that needs documenting. All are similar. + if(ProfExotic == TRUE)// We are proficient in exotics... + { + CurrentItemDamage = i16;// Set max damage. + CurrentItemValue += // We add onto the current value some things... + (CurrentItemDamage + // The damage (maximum) done by it. + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DIRE_MACE)) + // Adds 1 if specailised in it + (GetHasFeat(FEAT_IMPROVED_CRITICAL_DIRE_MACE) * i2) +// Adds 2 if can do good criticals in it + (GetHasFeat(FEAT_WEAPON_FOCUS_DIRE_MACE) * i2)); // Adds 2 if we do +2 damage with it + // If a very big creature - set as a primary weopen + if(CreatureSize >= CREATURE_SIZE_LARGE)//4+ + { + SetPrimaryWeapon(oTarget, oItem); + } + // If a medium creature - set as a two-handed weopen + else if(CreatureSize == CREATURE_SIZE_MEDIUM)//=3 + { + // Add 16 more for a "second" weapon. + CurrentItemValue += CurrentItemDamage; + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_DOUBLEAXE: + { + if(ProfExotic == TRUE) + { + CurrentItemDamage = i16;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DOUBLE_AXE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_DOUBLE_AXE) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_DOUBLE_AXE) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE)//4+ + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM)//=3 + { + // Add 16 more for a "second" weapon. + CurrentItemValue += i16; + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_TWOBLADEDSWORD: + { + if(ProfExotic == TRUE) + { + CurrentItemDamage = i16;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_TWO_BLADED_SWORD)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_TWO_BLADED_SWORD) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_TWO_BLADED_SWORD) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + CurrentItemValue += CurrentItemDamage; + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_GREATAXE: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i12;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_GREAT_AXE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_GREAT_AXE) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_AXE) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_GREATSWORD: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i12;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_GREAT_SWORD)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_GREAT_SWORD) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_GREAT_SWORD) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_HALBERD: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i10;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HALBERD)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_HALBERD) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_HALBERD) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_HEAVYFLAIL: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i10;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HEAVY_FLAIL)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_HEAVY_FLAIL) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_FLAIL) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_SCYTHE: + { + if(ProfExotic == TRUE) + { + CurrentItemDamage = i10;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SCYTHE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SCYTHE) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SCYTHE) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_SHORTSPEAR: + { + if(ProfSimple == TRUE || ProfDruid == TRUE) + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SPEAR)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SPEAR) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SPEAR) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + // Note: Should work, should be the same!! + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_MAGICSTAFF: + { + if(ProfWizard == TRUE || ProfSimple == TRUE || ProfRogue == TRUE || + ProfMonk == TRUE || ProfDruid == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_STAFF)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_STAFF) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_STAFF) * i2)); + if(CreatureSize >= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_MEDIUM) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_LONGBOW: + { + if(CreatureSize >= CREATURE_SIZE_MEDIUM && + (ProfMartial == TRUE || ProfElf == TRUE)) + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (CurrentItemIsMighty * CreatureStrength) + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LONGBOW)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_LONGBOW) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_LONGBOW) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_TOWERSHIELD: + { + if(ProfShield == TRUE && + CreatureSize >= CREATURE_SIZE_MEDIUM) + { + CurrentItemValue += GetItemACValue(oItem); + StoreShield(oTarget, oItem); + } + } + break; + } +} + +//:://///////////////////////////////////////////// +//:: Name BaseMediumWeapons +//::////////////////////////////////////////////// +/* + Adds the damage to the value +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void BaseMediumWeapons(object oTarget, object oItem) +{ + switch (CurrentItemType) + { + case BASE_ITEM_BASTARDSWORD: + { + if(ProfExotic == TRUE) + { + CurrentItemDamage = i10;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_BASTARD_SWORD) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_BASTARD_SWORD) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_BATTLEAXE: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_BATTLE_AXE) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_BATTLE_AXE) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_DWARVENWARAXE: + { + if(ProfExotic == TRUE) + { + CurrentItemDamage = i10;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DWAXE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_DWAXE) * i2) + + (GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_DWAXE) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + case BASE_ITEM_CLUB: + { + if(ProfWizard == TRUE || ProfSimple == TRUE || + ProfMonk == TRUE || ProfDruid == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_CLUB)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_CLUB) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_CLUB) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_KATANA: + { + if(ProfExotic == TRUE) + { + CurrentItemDamage = i10;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_KATANA)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_KATANA) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_KATANA) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_LIGHTFLAIL: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_FLAIL)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_FLAIL) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_FLAIL) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_LONGSWORD: + { + if(ProfMartial == TRUE || ProfElf == TRUE) + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LONG_SWORD)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_LONG_SWORD) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_LONG_SWORD) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_MORNINGSTAR: + { + if(ProfSimple == TRUE || ProfRogue == TRUE) // Primary only + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_MORNING_STAR)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_MORNING_STAR) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_MORNING_STAR) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_RAPIER: + { + if(ProfRogue == TRUE || ProfMartial == TRUE || ProfElf == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_RAPIER)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_RAPIER) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_RAPIER) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_SCIMITAR: + { + if(ProfMartial == TRUE || ProfDruid == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SCIMITAR)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SCIMITAR) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SCIMITAR) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_WARHAMMER: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + GetHasFeat(FEAT_WEAPON_SPECIALIZATION_WAR_HAMMER) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_WAR_HAMMER) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_WAR_HAMMER) * i2)); + if(CreatureSize >= CREATURE_SIZE_MEDIUM) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_SMALL) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_HEAVYCROSSBOW: + { + if(CreatureSize >= CREATURE_SIZE_SMALL && + (ProfWizard == TRUE || ProfSimple == TRUE || + ProfRogue == TRUE || ProfMonk == TRUE)) + { + CurrentItemDamage = i10;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (CurrentItemIsMighty * CreatureStrength) + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HEAVY_CROSSBOW)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_HEAVY_CROSSBOW) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_SHORTBOW: + { + if(CreatureSize >= CREATURE_SIZE_SMALL && + (ProfRogue == TRUE || ProfMartial == TRUE || ProfElf == TRUE)) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (CurrentItemIsMighty * CreatureStrength) + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHORTBOW)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SHORTBOW) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SHORTBOW) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_LARGESHIELD: + { + if(CreatureSize >= CREATURE_SIZE_SMALL && + ProfShield == TRUE) + { + CurrentItemValue += GetItemACValue(oItem); + StoreShield(oTarget, oItem); + } + } + break; + } +} + +//:://///////////////////////////////////////////// +//:: Name BaseSmallWeapons +//::////////////////////////////////////////////// +/* + Adds the damage to the value...then sets it +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// +void BaseSmallWeapons(object oTarget, object oItem) +{ + switch (CurrentItemType) + { + case BASE_ITEM_HANDAXE: + { + if(ProfMonk == TRUE || ProfMartial == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_HAND_AXE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_HAND_AXE) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_HAND_AXE) * i2)); + if(CreatureSize >= CREATURE_SIZE_SMALL && + CreatureSize <= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_TINY) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_KAMA: + { + if(ProfMonk == TRUE || ProfExotic == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_KAMA)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_KAMA) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_KAMA) * i2)); + if(CreatureSize >= CREATURE_SIZE_SMALL && + CreatureSize <= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_TINY) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_LIGHTHAMMER: + { + if(ProfMartial == TRUE) + { + CurrentItemDamage = i4;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_HAMMER) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_HAMMER) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_HAMMER) * i2)); + if(CreatureSize >= CREATURE_SIZE_SMALL && + CreatureSize <= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_TINY) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_LIGHTMACE: + { + if(ProfSimple == TRUE || ProfRogue == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_MACE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_MACE) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_MACE) * i2)); + if(CreatureSize >= CREATURE_SIZE_SMALL && + CreatureSize <= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_TINY) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_SHORTSWORD: + { + if(ProfRogue == TRUE || ProfMartial == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHORT_SWORD)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SHORT_SWORD) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SHORT_SWORD) * i2)); + if(CreatureSize >= CREATURE_SIZE_SMALL && + CreatureSize <= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_TINY) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_WHIP: + { + if(ProfExotic == TRUE) + { + CurrentItemDamage = i2;// Set max damage. + CurrentItemValue += CurrentItemDamage; + // We add a special amount, 10, as it is only used as a secondary + // weapon, and only in the offhand. + CurrentItemValue += i10; + if(CreatureSize >= CREATURE_SIZE_SMALL && + CreatureSize <= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_SICKLE: + { + if(ProfSimple == TRUE || ProfDruid == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SICKLE)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SICKLE) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SICKLE) * i2)); + if(CreatureSize >= CREATURE_SIZE_SMALL && + CreatureSize <= CREATURE_SIZE_LARGE) + { + SetPrimaryWeapon(oTarget, oItem); + } + else if(CreatureSize == CREATURE_SIZE_TINY) + { + SetTwoHandedWeapon(oTarget, oItem); + } + } + } + break; + case BASE_ITEM_DART: + { + // Ranged weapons below + if(CreatureSize <= CREATURE_SIZE_LARGE && + (ProfSimple == TRUE || ProfRogue == TRUE || ProfDruid == TRUE)) + { + CurrentItemDamage = i4;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (CurrentItemIsMighty * CreatureStrength) + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DART)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_DART) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_DART) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_LIGHTCROSSBOW: + { + if(CreatureSize <= CREATURE_SIZE_LARGE && + (ProfWizard == TRUE || ProfSimple == TRUE || + ProfRogue == TRUE || ProfMonk == TRUE)) + { + CurrentItemDamage = i8;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (CurrentItemIsMighty * CreatureStrength) + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_LIGHT_CROSSBOW)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_LIGHT_CROSSBOW) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_SLING: + { + if(CreatureSize <= CREATURE_SIZE_LARGE && + (ProfSimple == TRUE || ProfMonk == TRUE || ProfDruid == TRUE)) + { + CurrentItemDamage = i4;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (CurrentItemIsMighty * CreatureStrength) + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SLING)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SLING) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SLING) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_THROWINGAXE: + { + if(CreatureSize <= CREATURE_SIZE_LARGE && ProfMartial == TRUE) + { + CurrentItemDamage = i6;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (CreatureStrength) + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SLING)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SLING) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SLING) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_SMALLSHIELD: + { + if(ProfShield) + { + CurrentItemValue += GetItemACValue(oItem); + StoreShield(oTarget, oItem); + } + } + break; + } +} + +//:://///////////////////////////////////////////// +//:: Name BaseTinyWeapons +//::////////////////////////////////////////////// +/* + Adds damage to the value, and sets it. +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void BaseTinyWeapons(object oTarget, object oItem) +{ + switch (CurrentItemType) + { + case BASE_ITEM_DAGGER: + { + if(CreatureSize <= CREATURE_SIZE_MEDIUM && + (ProfWizard == TRUE || ProfSimple == TRUE || ProfRogue == TRUE || + ProfMonk == TRUE || ProfDruid == TRUE)) + { + CurrentItemDamage = i4;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_DAGGER)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_DAGGER) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_DAGGER) * i2)); + SetPrimaryWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_KUKRI: + { + if(CreatureSize <= CREATURE_SIZE_MEDIUM && ProfExotic == TRUE) + { + CurrentItemDamage = i4;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_KUKRI)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_KUKRI) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_KUKRI) * i2)); + SetPrimaryWeapon(oTarget, oItem); + } + } + break; + case BASE_ITEM_SHURIKEN: + { + // Ranged weapons below + if(CreatureSize <= CREATURE_SIZE_MEDIUM && + (ProfMonk == TRUE || ProfExotic == TRUE)) + { + CurrentItemDamage = i3;// Set max damage. + CurrentItemValue += (CurrentItemDamage + + (GetHasFeat(FEAT_WEAPON_SPECIALIZATION_SHURIKEN)) + + (GetHasFeat(FEAT_IMPROVED_CRITICAL_SHURIKEN) * i2) + + (GetHasFeat(FEAT_WEAPON_FOCUS_SHURIKEN) * i2)); + StoreRangedWeapon(oTarget, oItem); + } + } + break; + } +} +//:://///////////////////////////////////////////// +//:: Name SetPrimaryWeapon +//::////////////////////////////////////////////// +/* + If the value of the object is greater than the + stored one, set it. + If the weopen is of lesser value, and can deul + wield, then set it as a weopen that can be deul wielded. + + Re-written. Sets the objects into an array (at the end). +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void SetPrimaryWeapon(object oTarget, object oItem) +{ + // We insert the value into an array of all primary weapons, based + // on value. + if(CurrentItemType != BASE_ITEM_WHIP) // WHIPs are secondary only + { + ArrayOfWeapons(AI_WEAPON_PRIMARY, oTarget, oItem, CurrentItemValue); + } + // We also set up secondary array for all weapons which can be used well + // in the off hand. + // This takes some value off for size of weapon...depending on our size! + // IE to hit is lower, it is a lower value. + if(ProfTwoWeapons == TRUE && + // 4 = Light flail, 47 = Morningstar - NOT a valid second weapon. + CurrentItemType != BASE_ITEM_LIGHTFLAIL && + CurrentItemType != BASE_ITEM_MORNINGSTAR) + { + ArrayOfWeapons(AI_WEAPON_SECONDARY, oTarget, oItem, CurrentItemValue, TRUE); + } +} +//:://///////////////////////////////////////////// +//:: Name SetTwoHandedWeapon +//::////////////////////////////////////////////// +/* + Sets a two-handed weopen to use. +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void SetTwoHandedWeapon(object oTarget, object oItem) +{ + // We insert the value into an array of all 2 handed weapons, based + // on value. + ArrayOfWeapons(AI_WEAPON_TWO_HANDED, oTarget, oItem, CurrentItemValue); +} + +//:://///////////////////////////////////////////// +//:: Name SetRangedWeapon +//::////////////////////////////////////////////// +/* + Sets a ranged weopen - based on ammo as well + We only set one (until we don't use it) and in the AI + checks for ammo. Important - the default AI does't do that + well, and just equips a HTH weapon instead! + - Name SetAmmoCounters + Used to check ammo - for setting ranged weopen + - Name StoreRangedWeapon + First part of setting ranged weopen. Stores it! + It needs to check for ammo when it is set, you see +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void StoreRangedWeapon(object oTarget, object oItem) +{ + int nNth = GetLocalInt(oTarget, SETWEP_DISTANCE); + nNth++; + string sNth = IntToString(nNth); + // Special: If unlimited ammo, we will use regardless of ammo. + SetLocalInt(oItem, SETWEP_IS_UNLIMITED, CurrentItemIsUnlimited); + SetLocalInt(oItem, SETWEP_VALUE, CurrentItemValue); + SetLocalObject(oTarget, SETWEP_DISTANCE + sNth, oItem); + SetLocalInt(oTarget, SETWEP_DISTANCE, nNth); +} + +void SetAmmoCounters(object oTarget) +{ + switch(CurrentItemType) + { + case BASE_ITEM_ARROW: + { + HasArrows = TRUE; + return; + } + break; + case BASE_ITEM_BOLT: + { + HasBolts = TRUE; + return; + } + break; + case BASE_ITEM_BULLET: + { + HasBullets = TRUE; + return; + } + break; + } +} + +void SetRangedWeapon(object oTarget) +{ + // Special: We set 2 weapons. The second just states there is a second + // and so we re-set weapons if we get round to using it. + int nNth = i1; + string sNth = IntToString(nNth); + object oItem = GetLocalObject(oTarget, SETWEP_DISTANCE + sNth); + int nBase, iHighestValueWeapon, iValue, iUnlimited, iShield, + iNextHighestValueWeapon, iHighestUnlimited, iAmmoSlot; + object oHighestItem, oNextHighestItem; + + while(GetIsObjectValid(oItem)) + { + nBase = GetBaseItemType(oItem); + iValue = GetLocalInt(oItem, SETWEP_VALUE); + iUnlimited = GetLocalInt(oItem, SETWEP_IS_UNLIMITED); + if(nBase == BASE_ITEM_DART || nBase == BASE_ITEM_SHURIKEN || + nBase == BASE_ITEM_THROWINGAXE) + // 31 = Dart, 59 = Shuriken, 63 = Throwing axe + { + //iHighestValueWeapon starts as 0, so + if(iValue > iHighestValueWeapon || + iHighestValueWeapon == i0) + { + iHighestValueWeapon = iValue; + oHighestItem = oItem; + iShield = TRUE; + // We set right hand, because it is a throwing weapon + iAmmoSlot = INVENTORY_SLOT_RIGHTHAND; + iHighestUnlimited = iUnlimited; + } + else if(iValue > iNextHighestValueWeapon || + iNextHighestValueWeapon == i0) + { + iNextHighestValueWeapon = iValue; + oNextHighestItem = oItem; + } + } + else if(nBase == BASE_ITEM_HEAVYCROSSBOW || + nBase == BASE_ITEM_LIGHTCROSSBOW)// 6 = Heavy, 7 = Light X-bow + { + if(HasBolts == TRUE || iUnlimited == TRUE) + { + if(iValue > iHighestValueWeapon || + iHighestValueWeapon == i0) + { + iHighestValueWeapon = iValue; + oHighestItem = oItem; + iAmmoSlot = INVENTORY_SLOT_BOLTS; + iShield = FALSE; + iHighestUnlimited = iUnlimited; + } + else if(iValue > iNextHighestValueWeapon || + iNextHighestValueWeapon == i0) + { + iNextHighestValueWeapon = iValue; + oNextHighestItem = oItem; + } + } + } + else if(nBase == BASE_ITEM_LONGBOW || + nBase == BASE_ITEM_SHORTBOW)// 8 = Long, 11 = Short bow + { + if(HasArrows == TRUE || iUnlimited == TRUE) + { + if(iValue > iHighestValueWeapon || + iHighestValueWeapon == i0) + { + iHighestValueWeapon = iValue; + oHighestItem = oItem; + iShield = FALSE; + iAmmoSlot = INVENTORY_SLOT_ARROWS; + iHighestUnlimited = iUnlimited; + } + else if(iValue > iNextHighestValueWeapon || + iNextHighestValueWeapon == i0) + { + iNextHighestValueWeapon = iValue; + oNextHighestItem = oItem; + } + } + } + else if(nBase == BASE_ITEM_SLING)// 61 = Sling + { + if(HasBullets == TRUE || iUnlimited == TRUE) + { + if(iValue > iHighestValueWeapon || + iHighestValueWeapon == i0) + { + iHighestValueWeapon = iValue; + oHighestItem = oItem; + iShield = TRUE; + iAmmoSlot = INVENTORY_SLOT_BULLETS; + iHighestUnlimited = iUnlimited; + } + else if(iValue > iNextHighestValueWeapon || + iNextHighestValueWeapon == i0) + { + iNextHighestValueWeapon = iValue; + oNextHighestItem = oItem; + } + } + } + DeleteLocalInt(oItem, SETWEP_VALUE); + DeleteLocalInt(oItem, SETWEP_IS_UNLIMITED); + DeleteLocalObject(oTarget, SETWEP_DISTANCE + sNth); + nNth++; + sNth = IntToString(nNth); + oItem = GetLocalObject(oTarget, SETWEP_DISTANCE + sNth); + } + // No setting if not valid! + if(GetIsObjectValid(oHighestItem)) + { + SWFinalAIObject(oTarget, AI_WEAPON_RANGED, oHighestItem); + SWFinalAIInteger(oTarget, AI_WEAPON_RANGED_AMMOSLOT, iAmmoSlot); + if(iHighestUnlimited) + { + SWFinalAIInteger(oTarget, AI_WEAPON_RANGED_IS_UNLIMITED, iHighestUnlimited); + } + // Can a shield be used with it? Default is 0, we only set non 0 values. + if(iShield) + { + SWFinalAIInteger(oTarget, AI_WEAPON_RANGED_SHIELD, iShield); + } + // No setting if not valid! + if(GetIsObjectValid(oNextHighestItem)) + { + SWFinalAIObject(oTarget, AI_WEAPON_RANGED_2, oNextHighestItem); + } + } +} + +//:://///////////////////////////////////////////// +//:: Name SetShield, StoreShield +//::////////////////////////////////////////////// +/* + V. Simple. If value is higher, set the shield +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +void StoreShield(object oTarget, object oItem) +{ + int nNth = GetLocalInt(oTarget, SETWEP_SHIELD); + nNth++; + string sNth = IntToString(nNth); + // Set the value, so we can use the top values again. + SetLocalInt(oItem, SETWEP_VALUE, CurrentItemValue); + SetLocalObject(oTarget, SETWEP_SHIELD + sNth, oItem); + SetLocalInt(oTarget, SETWEP_SHIELD, nNth); +} + +void SetShield(object oTarget) +{ + int nNth = i1; + string sNth = IntToString(nNth); + object oItem = GetLocalObject(oTarget, SETWEP_SHIELD + sNth); + int iHighestValueShield, iValue, iNextHighestValueShield; + object oHighestShield, oNextHighestShield; + + while(GetIsObjectValid(oItem)) + { + iValue = GetLocalInt(oItem, SETWEP_VALUE); + if(iValue > iHighestValueShield) + { + oHighestShield = oItem; + iHighestValueShield = iValue; + } + else if(iValue > iNextHighestValueShield) + { + oNextHighestShield = oItem; + iNextHighestValueShield = iValue; + } + DeleteLocalInt(oItem, SETWEP_VALUE); + DeleteLocalObject(oTarget, SETWEP_SHIELD + sNth); + nNth++; + sNth = IntToString(nNth); + // Get next shield + oItem = GetLocalObject(oTarget, SETWEP_SHIELD + sNth); + } + if(GetIsObjectValid(oHighestShield)) + { + SWFinalAIObject(oTarget, AI_WEAPON_SHIELD, oHighestShield); + // Need original if second + if(GetIsObjectValid(oNextHighestShield)) + { + SWFinalAIObject(oTarget, AI_WEAPON_SHIELD_2, oNextHighestShield); + } + } +} + +//:://///////////////////////////////////////////// +//:: Name GetWeoponSize +//::////////////////////////////////////////////// +/* + Returns the Base Weopen size of oItem +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// + +int GetWeaponSize(object oItem) +{ + switch(GetBaseItemType(oItem)) + { + // Tiny + // 22, 42, 59. + case BASE_ITEM_DAGGER: + case BASE_ITEM_KUKRI: + case BASE_ITEM_SHURIKEN: + return WEAPON_SIZE_TINY; + break; + // Small + // 0, 7, 9, 14, 31, 37, 38, 40, 60, 61, 63 + case BASE_ITEM_SHORTSWORD: + case BASE_ITEM_LIGHTCROSSBOW: + case BASE_ITEM_LIGHTMACE: + case BASE_ITEM_SMALLSHIELD: + case BASE_ITEM_DART: + case BASE_ITEM_LIGHTHAMMER: + case BASE_ITEM_HANDAXE: + case BASE_ITEM_KAMA: + case BASE_ITEM_SICKLE: + case BASE_ITEM_SLING: + case BASE_ITEM_THROWINGAXE: + case BASE_ITEM_WHIP: // Hordes + return WEAPON_SIZE_SMALL; + break; + // Medium + // 1, 2, 3, 4, 5, 6, 11, 28, 41, 47, 51, 53, 56 + // 1-6 = + // BASE_ITEM_LONGSWORD, BASE_ITEM_BATTLEAXE, BASE_ITEM_BASTARDSWORD + // BASE_ITEM_LIGHTFLAIL, BASE_ITEM_WARHAMMER, BASE_ITEM_HEAVYCROSSBOW + case BASE_ITEM_LONGSWORD: + case BASE_ITEM_BATTLEAXE: + case BASE_ITEM_BASTARDSWORD: + case BASE_ITEM_LIGHTFLAIL: + case BASE_ITEM_WARHAMMER: + case BASE_ITEM_HEAVYCROSSBOW: + case BASE_ITEM_SHORTBOW: + case BASE_ITEM_CLUB: + case BASE_ITEM_KATANA: + case BASE_ITEM_MORNINGSTAR: + case BASE_ITEM_RAPIER: + case BASE_ITEM_SCIMITAR: + case BASE_ITEM_LARGESHIELD: + case BASE_ITEM_DWARVENWARAXE: // Hordes + return WEAPON_SIZE_MEDIUM; + break; + // Large weapons + // 8, 10, 12, 13, 18, 32, 33, 35, 45, 50, 55, 57, 58 + case BASE_ITEM_LONGBOW: + case BASE_ITEM_HALBERD: + case BASE_ITEM_TWOBLADEDSWORD: + case BASE_ITEM_GREATSWORD: + case BASE_ITEM_GREATAXE: + case BASE_ITEM_DIREMACE: + case BASE_ITEM_DOUBLEAXE: + case BASE_ITEM_HEAVYFLAIL: + case BASE_ITEM_MAGICSTAFF: + case BASE_ITEM_QUARTERSTAFF: + case BASE_ITEM_SCYTHE: + case BASE_ITEM_TOWERSHIELD: + case BASE_ITEM_SHORTSPEAR: + return WEAPON_SIZE_LARGE; + break; + } + return FALSE; +} + +//:://///////////////////////////////////////////// +//:: Name DeleteInts +//::////////////////////////////////////////////// +/* + Deletes everything, like what weopen they are using + and what proficiencies they have that may be stored. +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// +void DeleteInts(object oTarget) +{ + // Deletes the INT's, for values, in the array's. + DeleteValueInts(oTarget, AI_WEAPON_PRIMARY); + DeleteValueInts(oTarget, AI_WEAPON_SECONDARY); + DeleteValueInts(oTarget, AI_WEAPON_TWO_HANDED); + // Missile and shield ones. + DeleteLocalInt(oTarget, SETWEP_DISTANCE); + DeleteLocalInt(oTarget, SETWEP_SHIELD); +} + +//:://///////////////////////////////////////////// +//:: Name ArrayOfWeapons, MoveArrayBackOne +//::////////////////////////////////////////////// +/* + This is the arrays, IE sets up what weapons + we have value first, good for searching through + later and +*/ +//::////////////////////////////////////////////// +//:: Created By: Yrean +//:: Modified By: Jasperre +//::////////////////////////////////////////////// +void ArrayOfWeapons(string sArray, object oTarget, object oItem, int iValue, int iSecondary = FALSE) +{ + // Check for if it is secondary. + // We add some value based on the creature size against weapon size. + // - We also may take away some. + int iSetValue = iValue; + if(iSecondary == TRUE) + { + // We take 2 or add 2 for different sizes. Not too much...but enough? + iSetValue += ((CreatureSize - CurrentItemSize) * i2); + } + int iOtherItemsValues, i, iBreak; + int iMax = GetLocalInt(oTarget, MAXINT_ + sArray); + string sArrayStore; + // Special - no max items! + if(iMax < i1) + { + sArrayStore = sArray + s1; + SetLocalInt(oTarget, sArrayStore, iSetValue); + SetLocalInt(oTarget, sArrayStore + WEAP_SIZE, CurrentItemSize); + SetLocalInt(oTarget, sArrayStore + WEAP_DAMAGE, CurrentItemDamage); + SetLocalObject(oTarget, sArrayStore, oItem); + iMax++; + SetLocalInt(oTarget, MAXINT_ + sArray, iMax); + } + // Else, we will set it in the array. + else + { + // Loop through the items stored already. + for(i = i1; (i <= iMax && iBreak != TRUE); i++) + { + // Get the value of the item. + iOtherItemsValues = GetLocalInt(oTarget, sArray + IntToString(i)); + // If imput is greater than stored...move all of them back one. + if(iValue > iOtherItemsValues) + { + // Set weapon size as well. + sArrayStore = sArray + IntToString(i); + MoveArrayBackOne(sArray, i, oTarget, iMax); + SetLocalInt(oTarget, sArrayStore, iSetValue); + SetLocalInt(oTarget, sArrayStore + WEAP_SIZE, CurrentItemSize); + SetLocalInt(oTarget, sArrayStore + WEAP_DAMAGE, CurrentItemDamage); + SetLocalObject(oTarget, sArrayStore, oItem); + iMax++; + SetLocalInt(oTarget, MAXINT_ + sArray, iMax); + iBreak = TRUE; + } + // If end, we set to the end :-) + else if(i == iMax) + { + // Set weapon size as well. Add one to i to be at the end. + sArrayStore = sArray + IntToString(i + i1); + SetLocalInt(oTarget, sArrayStore, iSetValue); + SetLocalInt(oTarget, sArrayStore + WEAP_SIZE, CurrentItemSize); + SetLocalInt(oTarget, sArrayStore + WEAP_DAMAGE, CurrentItemDamage); + SetLocalObject(oTarget, sArrayStore, oItem); + iMax++; + SetLocalInt(oTarget, MAXINT_ + sArray, iMax); + iBreak = TRUE; + } + } + } +} + +void MoveArrayBackOne(string sArray, int iNumberStart, object oTarget, int iMax) +{ + // Get the first item... + object oItemAtNumber; + string sCurrentName, sNewName; + int iItemAtNumberValue, i, iCurrentItemSize, iCurrentItemDamage; + // Move it from the back, back one, then then next... + for(i = iMax; i >= iNumberStart; i--) + { + // Sets the name up right. + sCurrentName = sArray + IntToString(i); + sNewName = sArray + IntToString(i + i1); + // Set the things up in the right parts. + oItemAtNumber = GetLocalObject(oTarget, sCurrentName); + iItemAtNumberValue = GetLocalInt(oTarget, sCurrentName); + iCurrentItemSize = GetLocalInt(oTarget, sCurrentName + WEAP_SIZE); + iCurrentItemDamage = GetLocalInt(oTarget, sCurrentName + WEAP_DAMAGE); + // To the NEW name - we add one to the i value. + SetLocalObject(oTarget, sNewName, oItemAtNumber); + SetLocalInt(oTarget, sNewName, iItemAtNumberValue); + SetLocalInt(oTarget, sNewName + WEAP_SIZE, iCurrentItemSize); + SetLocalInt(oTarget, sNewName + WEAP_DAMAGE, iCurrentItemSize); + } +} +void DeleteDatabase(object oTarget, string sArray) +{ + int iMax = GetLocalInt(oTarget, MAXINT_ + sArray); + int i; + string sNewName; + if(iMax) + { + for(i = i1; i <= iMax; i++) + { + sNewName = sArray + IntToString(i); + DeleteLocalObject(oTarget, sNewName);// Object + DeleteLocalInt(oTarget, sNewName);// Value + DeleteLocalInt(oTarget, sNewName + WEAP_SIZE);// Size + DeleteLocalInt(oTarget, sNewName + WEAP_DAMAGE);// Damage + } + } + // Here, we do delete the max + DeleteLocalInt(oTarget, MAXINT_ + sArray); +} +void DeleteValueInts(object oTarget, string sArray) +{ + int iMax = GetLocalInt(oTarget, MAXINT_ + sArray); + int i; + if(iMax) + { + for(i = i1; i <= iMax; i++) + { + DeleteLocalInt(oTarget, sArray + IntToString(i)); + } + } + // Note: We keep the size... +} + +void SWFinalAIObject(object oTarget, string sName, object oObject) +{ + SetLocalObject(oTarget, AI_OBJECT + sName, oObject); +} +void SWFinalAIInteger(object oTarget, string sName, int iInt) +{ + SetLocalInt(oTarget, AI_INTEGER + sName, iInt); +} +void SWDeleteAIObject(object oTarget, string sName) +{ + DeleteLocalObject(oTarget, AI_OBJECT + sName); +} +void SWDeleteAIInteger(object oTarget, string sName) +{ + DeleteLocalInt(oTarget, AI_INTEGER + sName); +} + +// reset healing kits only on oTarget. +void ResetHealingKits(object oTarget) +{ + object oItem, oHighestKit; + int iHealingKitsAmount, iItemValue; + int iRunningValue = i0; // For kits + // The inventory + oItem = GetFirstItemInInventory(oTarget); + while(GetIsObjectValid(oItem)) + { + if(GetBaseItemType(oItem) == BASE_ITEM_HEALERSKIT) + { + iHealingKitsAmount++; + iItemValue = GetGoldPieceValue(oItem); + // Stacked kits be worth what they should be seperatly. + iItemValue = iItemValue/GetNumStackedItems(oItem); + if(iItemValue > iRunningValue) + { + iRunningValue = iItemValue; + oHighestKit = oItem; + } + } + oItem = GetNextItemInInventory(oTarget); + } + // Need some, any! + if(iHealingKitsAmount > i0) + { + // set healing kits (if any) + SWFinalAIObject(oTarget, AI_VALID_HEALING_KIT_OBJECT, oHighestKit); + // Set amount left + SWFinalAIInteger(oTarget, AI_VALID_HEALING_KITS, iHealingKitsAmount); + } +} + +void DeleteAllPreviousWeapons(object oTarget) +{ + DeleteDatabase(oTarget, AI_WEAPON_PRIMARY); + DeleteDatabase(oTarget, AI_WEAPON_SECONDARY); + DeleteDatabase(oTarget, AI_WEAPON_TWO_HANDED); + + SWDeleteAIInteger(oTarget, AI_WEAPON_RANGED_SHIELD); + SWDeleteAIInteger(oTarget, AI_WEAPON_RANGED_IS_UNLIMITED); + SWDeleteAIInteger(oTarget, AI_WEAPON_RANGED_AMMOSLOT); + SWDeleteAIObject(oTarget, AI_WEAPON_RANGED); + SWDeleteAIObject(oTarget, AI_WEAPON_RANGED_2); + SWDeleteAIObject(oTarget, AI_WEAPON_SHIELD); + SWDeleteAIObject(oTarget, AI_WEAPON_SHIELD_2); +} + +// Special: Apply EffectCutsceneImmobilize +void AI_SpecialActionApplyItem(object oTarget) +{ + ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneImmobilize(), oTarget); +} +// Special: Remove EffectCutsceneImmobilize +void AI_SpecialActionRemoveItem(object oTarget) +{ + effect eCheck = GetFirstEffect(oTarget); + while(GetIsEffectValid(eCheck)) + { + if(GetEffectType(eCheck) == EFFECT_TYPE_CUTSCENEIMMOBILIZE && + GetEffectSpellId(eCheck) == iM1) RemoveEffect(oTarget, eCheck); + eCheck = GetNextEffect(oTarget); + } +} +// Gets a item talent value +// - iTalent, 1-21. +void AI_SetItemTalentValue(int iTalent) +{ + // We are already EffectCutsceneImmobilized + + // Simply get the best. + talent tCheck = GetCreatureTalentBest(iTalent, i20); + int iValue = GetIdFromTalent(tCheck); + + // Set to value. + SetAIConstant(ITEM_TALENT_VALUE + IntToString(iTalent), iValue); +} + +//void main(){ SetWeapons(); } diff --git a/_content/_hak/rune_prc8_top/j_inc_spawnin.nss b/_content/_hak/rune_prc8_top/j_inc_spawnin.nss new file mode 100644 index 0000000..7b10fbd --- /dev/null +++ b/_content/_hak/rune_prc8_top/j_inc_spawnin.nss @@ -0,0 +1,1237 @@ +/************************ [Spawn In Include] *********************************** + Filename: J_Inc_SpawnIn +************************* [Spawn In Include] *********************************** + This contains all the functions used in the spawning process, in one easy + and very long file. + + It also importantly sets up spells we have, or at least talents, so we + know if we have any spells from category X. +************************* [History] ******************************************** + 1.3 - Changed, added a lot of new things (such as constants file) +************************* [Workings] ******************************************* + This doesn't call anything to run the rest, except AI_SetUpEndOfSpawn has + a lot of things that the generic AI requires (SetListeningPatterns and skills + and waypoints ETC) + + See the spawn in script for all the actual uses. +************************* [Arguments] ****************************************** + Arguments: N/A see spawn in script +************************* [Spawn In Include] **********************************/ + +// All constants. +#include "j_inc_setweapons" +// Set weapons +// - Constants file is in this + +// Special: Bioware SoU Waypoints/Animations constants +// - Here so I know where they are :-P +const string sAnimCondVarname = "NW_ANIM_CONDITION"; +// If set, the NPC is civilized +const int NW_ANIM_FLAG_IS_CIVILIZED = 0x00000400; +// If set, the NPC will use voicechats +const int NW_ANIM_FLAG_CHATTER = 0x00000004; +// If set, the NPC is mobile in a close-range +const int NW_ANIM_FLAG_IS_MOBILE_CLOSE_RANGE = 0x00000200; + +/******************************************************************************/ +// Functions: +/******************************************************************************/ +// This will activate one aura, very quickly. +// If we have more than one...oh well. +void AI_AdvancedAuras(); +// Activate the aura number, if it is possible. +void AI_ActivateAura(int nAuraNumber); +// This is an attempt to speed up some things. +// We use talents to set general valid categories. +// Levels are also accounted for, checking the spell given and using a switch +// statement to get the level. +void AI_SetUpSpells(); + +// Base for moving round thier waypoints +// - Uses ExectuteScript to run the waypoint walking. +void SpawnWalkWayPoints(int nRun = FALSE, float fPause = 1.0); + +// Sets up what we will listen to (everything!) +void AI_SetListeningPatterns(); +// This will set what creature to create OnDeath. +void AI_SetDeathResRef(string sResRef); +// This will set the string, sNameOfValue, to sValue. Array size of 1. +// - Use iPercentToSay to determine what % out of 100 it is said. +void AI_SetSpawnInSpeakValue(string sNameOfValue, string sValue, int iPercentToSay = 100); +// This will choose a random string, using iAmountOfValues, which is +// the amount of non-empty strings given. The size of the array is therefore 1. +// - Use iPercentToSay to determine what % out of 100 it is said. +void AI_SetSpawnInSpeakRandomValue(string sNameOfValue, int iPercentToSay, int iAmountOfValues, string sValue1, string sValue2, string sValue3 = "", string sValue4 = "", string sValue5 = "", string sValue6 = "", string sValue7 = "", string sValue8 = "", string sValue9 = "", string sValue10 = "", string sValue11 = "", string sValue12 = ""); +// This will set an array of values, to sNameOfValue, for one to be chosen to +// be said at the right time :-) +// - sNameOfValue must be a valid name. +// - Use iPercentToSay to determine what % out of 100 it is said. +// NOTE: If the sNameOfValue is any combat one, we make that 1/100 to 1/1000. +void AI_SetSpawnInSpeakArray(string sNameOfValue, int iPercentToSay, int iSize, string sValue1, string sValue2, string sValue3 = "", string sValue4 = "", string sValue5 = "", string sValue6 = "", string sValue7 = "", string sValue8 = "", string sValue9 = "", string sValue10 = "", string sValue11 = "", string sValue12 = ""); + +// This applies an increase, decrease or no change to the intended stat. +void AI_ApplyStatChange(int iStat, int iAmount); +// This will alter (magically) an ammount of random stats - iAmount +// by a value within iLowest and iHighest. +void AI_CreateRandomStats(int iLowest, int iHighest, int iAmount); +// This will randomise other stats. Put both numbers to 0 to ignore some. +// iHPMin, iHPMax = HP changes. +// iReflexSaveMin, iReflexSaveMax = Reflex Save changes +// iWillSaveMin, iWillSaveMax = Will Save changes +// iFortSaveMin, iFortSaveMax = Fortitude Save changes +// iACMin, iACMax = AC change. +// Use iACType to define the AC type - default AC_DODGE_BONUS +void AI_CreateRandomOther(int iHPMin, int iHPMax, int iReflexSaveMin = 0, int iReflexSaveMax = 0, int iWillSaveMin = 0, int iWillSaveMax = 0, int iFortSaveMin = 0, int iFortSaveMax = 0, int iACMin = 0, int iACMax = 0, int iACType = AC_DODGE_BONUS); + +// Sets up thier selection of skills, to integers, if they would ever use them. +// NOTE: it also triggers "hide" if they have enough skill and not stopped. +void AI_SetUpSkillToUse(); +// Sets the turning level if we have FEAT_TURN_UNDEAD. +// - Called from AI_SetUpEndOfSpawn. +void AI_SetTurningLevel(); +// This MUST be called. It fires these events: +// SetUpSpells, SetUpSkillToUse, SetListeningPatterns, SetWeapons, AdvancedAuras. +// These MUST be called! the AI might fail to work correctly if they don't fire! +void AI_SetUpEndOfSpawn(); +// This will make the visual effect passed play INSTANTLY at the creatures location. +// * iVFX - The visual effect constant number +void AI_SpawnInInstantVisual(int iVFX); +// This will make the visual effect passed play PERMAMENTLY. +// * iVFX - The visual effect constant number +// NOTE: They will be made to be SUPERNATUAL, so are not dispelled! +void AI_SpawnInPermamentVisual(int iVFX); +// This should not be used! +// * called from SetUpEndOfSpawn. +void AI_SetMaybeFearless(); +// This will set the MAXIMUM and MINIMUM targets to pass this stage (under sName) +// of the targeting system. IE: +// - If we set it to min of 5, max of 10, for AC, if we cancled 5 targets on having +// AC higher then wanted, we will take the highest 5 minimum. +// If there are over 10, we take a max of 10 to choose from. +// * iType - must be TARGET_HIGHER or TARGET_LOWER. Defaults to the lowest, so it +// targets the lowest value for sName. +void AI_SetAITargetingValues(string sName, int iType, int iMinimum, int iMaximum); + + +// Levels up us. +// * nLevel - Levels to this number (doesn't do anything if already GetHitDice >= nLevel). +// * nClass, nClass2, nClass3 - the 3 classes to level up in. Only needs 1 minimum +// Spreads equally if there is more then 1 nClass. +// Sets up spells to use automatically, but DOES NOT CHANGE CHALLENGE RATING! +void AI_LevelUpCreature(int nLevel, int nClass, int nClass2 = CLASS_TYPE_INVALID, int nClass3 = CLASS_TYPE_INVALID); + +// Used in AI_LevelUpCreature. +// - Levels up OBJECT_SELF, in nClass for nLevels +void AI_LevelLoop(int nClass, int nLevels); + +// Sets up what random spells to cheat-cast at the end of all known spells. +// it does NOT check for immunities, barriers, or anything else. +// - You can set spells to more then one of the imputs to have a higher % to cast that one. +void SetAICheatCastSpells(int iSpell1, int iSpell2, int iSpell3, int iSpell4, int iSpell5, int iSpell6); + +// Mark that the given creature has the given condition set for anitmations +// * Bioware SoU animations thing. +void SetAnimationCondition(int nCondition, int bValid = TRUE, object oCreature = OBJECT_SELF); + +// Sets we are a Beholder and use Ray attacks, and Animagic Ray. +//void SetBeholderAI(); +// Set we are a mindflayer, and uses some special AI for them. +///void SetMindflayerAI(); + +// This will set a spell trigger up. Under cirtain conditions, spells are released +// and cast on the caster. +// Once fired, a spell trigger is only reset by resting. Only 1 of each max is fired at once! +// * sType - is specifically: +// SPELLTRIGGER_DAMAGED_AT_PERCENT - When damaged, the trigger fires. Use iValue for the %. One at a time is fired. +// SPELLTRIGGER_IMMOBILE - Fired when held/paralyzed/sleeping ETC. One at a time is fired. +// SPELLTRIGGER_NOT_GOT_FIRST_SPELL - Makes sure !GetHasSpellEffect(iSpell1) already, +// then fires. Checks all in this category each round (first one fires!) +// SPELLTRIGGER_START_OF_COMBAT - Triggered always, at the start of DetermineCombatRound. +// * iNumber - can be 1-9, in sequential order of when you want them to fire. +// * iValue - is only required with DAMAGED_AT_PERCENT. +// * iSpellX - Cannot be 0. It should only really be defensive spells. +void SetSpellTrigger(string sType, int iValue, int iNumber, int iSpell1, int iSpell2 = 0, int iSpell3 = 0, int iSpell4 = 0, int iSpell5 = 0, int iSpell6 = 0, int iSpell7 = 0, int iSpell8 = 0, int iSpell9 = 0); + +// Mark that the given creature has the given condition set +void SetAnimationCondition(int nCondition, int bValid = TRUE, object oCreature = OBJECT_SELF) +{ + int nCurrentCond = GetLocalInt(oCreature, sAnimCondVarname); + if (bValid) { + SetLocalInt(oCreature, sAnimCondVarname, nCurrentCond | nCondition); + } else { + SetLocalInt(oCreature, sAnimCondVarname, nCurrentCond & ~nCondition); + } +} + +// Sets up what random spells to cheat-cast at the end of all known spells. +// it does NOT check for immunities, barriers, or anything else. +// - You can set spells to more then one of the imputs to have a higher % to cast that one. +void SetAICheatCastSpells(int iSpell1, int iSpell2, int iSpell3, int iSpell4, int iSpell5, int iSpell6) +{ + SetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(i1), iSpell1); + SetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(i2), iSpell2); + SetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(i3), iSpell3); + SetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(i4), iSpell4); + SetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(i5), iSpell5); + SetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(i6), iSpell6); +} + + +// Levels up us. +// * nLevel - Levels to this number (doesn't do anything if already GetHitDice >= nLevel). +// * nClass, nClass2, nClass3 - the 3 classes to level up in. Only needs 1 minimum +// - Spreads equally if there is more then 1 nClass. +// - Sets up spells to use automatically, but DOES NOT CHANGE CHALLENGE RATING! +// - Does NOT check for validness, or report any information. +void AI_LevelUpCreature(int nLevel, int nClass, int nClass2 = CLASS_TYPE_INVALID, int nClass3 = CLASS_TYPE_INVALID) +{ + // Divide by 1, 2 or 3 (100%, 50%, 33%) + int iDivideBy = (nClass >= i0) + (nClass2 >= i0) + (nClass3 >= i0); + + int iTotalPerClass = nLevel / iDivideBy; + + // Limit and loop - Class 1. + AI_LevelLoop(nClass, iTotalPerClass); + // 2 + AI_LevelLoop(nClass2, iTotalPerClass); + // 3 + AI_LevelLoop(nClass3, iTotalPerClass); +} + +// Used in AI_LevelUpCreature. +// - Levels up OBJECT_SELF, in iClass for nLevels +void AI_LevelLoop(int nClass, int nLevels) +{ + // Limit and loop + while(nLevels > FALSE) + { + LevelUpHenchman(OBJECT_SELF, nClass, TRUE); + nLevels--; + } +} + + +// This will set what creature to create OnDeath. +void AI_SetDeathResRef(string sResRef) +{ + SetLocalString(OBJECT_SELF, AI_WE_WILL_CREATE_ON_DEATH, sResRef); +} +// This will set the string, sNameOfValue, to sValue. Array size of 1. +// - Use iPercentToSay to determine what % out of 100 it is said. +void AI_SetSpawnInSpeakValue(string sNameOfValue, string sValue, int iPercentToSay) +{ + SetLocalString(OBJECT_SELF, sNameOfValue + s1, sValue); + // The array is 1 big! + SetLocalInt(OBJECT_SELF, ARRAY_SIZE + sNameOfValue, i1); + SetLocalInt(OBJECT_SELF, ARRAY_PERCENT + sNameOfValue, iPercentToSay); +} +// This will choose a random string, using iAmountOfValues, which is +// the amount of non-empty strings given. The size of the array is therefore 1. +// - Use iPercentToSay to determine what % out of 100 it is said. +void AI_SetSpawnInSpeakRandomValue(string sNameOfValue, int iPercentToSay, int iAmountOfValues, string sValue1, string sValue2, string sValue3, string sValue4, string sValue5, string sValue6, string sValue7, string sValue8, string sValue9, string sValue10, string sValue11, string sValue12) +{ + // Need a value amount of values! + if(iAmountOfValues) + { + int iRandomNum = Random(iAmountOfValues) -1; // take one, as it is 0 - X, not 1 - X + string sValueToUse; + switch(iRandomNum) + { + case(i0):{sValueToUse = sValue1;}break; + case(i1):{sValueToUse = sValue2;}break; + case(i2):{sValueToUse = sValue3;}break; + case(i3):{sValueToUse = sValue4;}break; + case(i4):{sValueToUse = sValue5;}break; + case(i5):{sValueToUse = sValue6;}break; + case(i6):{sValueToUse = sValue7;}break; + case(i7):{sValueToUse = sValue8;}break; + case(i8):{sValueToUse = sValue9;}break; + case(i9):{sValueToUse = sValue10;}break; + case(i10):{sValueToUse = sValue11;}break; + case(i11):{sValueToUse = sValue12;}break; + } + SetLocalString(OBJECT_SELF, sNameOfValue + s1, sValueToUse); + // The array is 1 big! + SetLocalInt(OBJECT_SELF, ARRAY_SIZE + sNameOfValue, i1); + SetLocalInt(OBJECT_SELF, ARRAY_PERCENT + sNameOfValue, iPercentToSay); + } +} +void AI_SetSpawnInSpeakArray(string sNameOfValue, int iPercentToSay, int iSize, string sValue1, string sValue2, string sValue3 = "", string sValue4 = "", string sValue5 = "", string sValue6 = "", string sValue7 = "", string sValue8 = "", string sValue9 = "", string sValue10 = "", string sValue11 = "", string sValue12 = "") +{ + if(iSize >= i1) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "1", sValue1); + if(iSize >= i2) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "2", sValue2); + if(iSize >= i3) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "3", sValue3); + if(iSize >= i4) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "4", sValue4); + if(iSize >= i5) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "5", sValue5); + if(iSize >= i6) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "6", sValue6); + if(iSize >= i7) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "7", sValue7); + if(iSize >= i8) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "8", sValue8); + if(iSize >= i9) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "9", sValue9); + if(iSize >= i10) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "10", sValue10); + if(iSize >= i11) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "11", sValue11); + if(iSize >= i12) + { + SetLocalString(OBJECT_SELF, sNameOfValue + "12", sValue12); + // Hehe, this looks not stright if you stare at it! :-P + } } } } } } } } } } } } + // The array is so big... + SetLocalInt(OBJECT_SELF, ARRAY_SIZE + sNameOfValue, iSize); + SetLocalInt(OBJECT_SELF, ARRAY_PERCENT + sNameOfValue, iPercentToSay); +} +// This applies an increase, decrease or no change to the intended stat. +void AI_ApplyStatChange(int iStat, int iAmount) +{ + if(iAmount != i0) + { + effect eChange; + if(iAmount < i0) + { + int iNewAmount = abs(iAmount); + eChange = SupernaturalEffect(EffectAbilityDecrease(iStat, iNewAmount)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + else + { + eChange = SupernaturalEffect(EffectAbilityIncrease(iStat, iAmount)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + } +} +// This will, eventually, choose X number of stats, and change them within the +// range given. +void AI_CreateRandomStats(int iLowest, int iHighest, int iAmount) +{ + if(iAmount > i0 && !(iLowest == i0 && iHighest == i0) && iHighest >= iLowest) + { + int iRange = iHighest - iLowest; + int iNumSlots = iAmount; + if(iNumSlots > i6) iNumSlots = i6; + int iNumLeft = i6; + // Walk through each stat and figure out what it's chance of being + // modified is. As an example, suppose we wanted to have 4 randomized + // abilities. We'd look at the first ability and it would have a 4 in 6 + // chance of being picked. Let's suppose it was, the next ability would + // have a 3 in 5 chance of being picked. If this next ability wasn't + // picked to be changed, the 3rd ability woud have a 3 in 4 chance of + // being picked and so on. + int iCnt; + int iChange; + for(iCnt = i0; (iNumSlots > i0) && (iCnt < i6); iCnt++) + { + if((iNumSlots == iNumLeft) || (Random(iNumLeft) < iNumSlots)) + { + iChange = Random(iRange) + iLowest; + AI_ApplyStatChange(iCnt, iChange); + iNumSlots--; + } + iNumLeft--; + } + } +} + +void AI_CreateRandomOther(int iHPMin, int iHPMax, int iReflexSaveMin = 0, int iReflexSaveMax = 0, int iWillSaveMin = 0, int iWillSaveMax = 0, int iFortSaveMin = 0, int iFortSaveMax = 0, int iACMin = 0, int iACMax = 0, int iACType = AC_DODGE_BONUS) +{ + int iRange, iChange, iNewChange; + effect eChange; + if(!(iHPMin == i0 && iHPMax == i0) && iHPMax >= iHPMin) + { + iRange = iHPMax - iHPMin; + iChange = Random(iRange) + iHPMin; + if(iChange > i0) + { + eChange = SupernaturalEffect(EffectTemporaryHitpoints(iChange)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + else if(iChange < i0 && GetMaxHitPoints() > i1) + { + eChange = EffectDamage(iChange, DAMAGE_TYPE_DIVINE, DAMAGE_POWER_PLUS_FIVE); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + } + if(!(iReflexSaveMin == i0 && iReflexSaveMax == i0) && iReflexSaveMax >= iReflexSaveMin) + { + iRange = iReflexSaveMax - iReflexSaveMin; + iChange = Random(iRange) + iReflexSaveMin; + if(iChange > i0) + { + eChange = SupernaturalEffect(EffectSavingThrowIncrease(SAVING_THROW_REFLEX, iChange)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + else if(iChange < i0 && GetReflexSavingThrow(OBJECT_SELF) > i1) + { + iNewChange = abs(iChange); + eChange = SupernaturalEffect(EffectSavingThrowDecrease(SAVING_THROW_REFLEX, iNewChange)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + } + if(!(iWillSaveMin == i0 && iWillSaveMax == i0) && iWillSaveMax >= iWillSaveMin) + { + iRange = iWillSaveMax - iWillSaveMin; + iChange = Random(iRange) + iWillSaveMin; + if(iChange > i0) + { + eChange = SupernaturalEffect(EffectSavingThrowIncrease(SAVING_THROW_WILL, iChange)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + else if(iChange < i0 && GetWillSavingThrow(OBJECT_SELF) > i1) + { + iNewChange = abs(iChange); + eChange = SupernaturalEffect(EffectSavingThrowDecrease(SAVING_THROW_WILL, iNewChange)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + } + if(!(iFortSaveMin == i0 && iFortSaveMax == i0) && iFortSaveMax >= iFortSaveMin) + { + iRange = iFortSaveMax - iFortSaveMin; + iChange = Random(iRange) + iFortSaveMin; + if(iChange > i0) + { + eChange = SupernaturalEffect(EffectSavingThrowIncrease(SAVING_THROW_FORT, iChange)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + else if(iChange < i0 && GetFortitudeSavingThrow(OBJECT_SELF) > 1) + { + iNewChange = abs(iChange); + eChange = SupernaturalEffect(EffectSavingThrowDecrease(SAVING_THROW_FORT, iNewChange)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + } + if(!(iACMin == i0 && iACMax == i0) && iACMax >= iACMin) + { + iRange = iACMax - iACMin; + iChange = Random(iRange) + iACMin; + if(iChange > i0) + { + eChange = SupernaturalEffect(EffectACIncrease(iChange, iACType)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + else if(iChange < i0) + { + iNewChange = abs(iChange); + eChange = SupernaturalEffect(EffectACDecrease(iNewChange, iACType)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eChange, OBJECT_SELF); + } + } +} + +/*:://///////////////////////////////////////////// +//:: SetListeningPatterns +//::////////////////////////////////////////////// + Changed a lot. added in "**" (all) listening, for hearing enemies. +//::////////////////////////////////////////////*/ + +void AI_SetListeningPatterns() +{ + // Lag check + if(GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_LISTENING, AI_OTHER_MASTER)) return; + SetListening(OBJECT_SELF, TRUE); +// Anyone that can hear it, and is not fighting, comes and helps + SetListenPattern(OBJECT_SELF, I_WAS_ATTACKED, i1); + //Set a custom listening pattern for the creature so that placables with + //"NW_BLOCKER" + Blocker NPC Tag will correctly call to their blockers. + string sBlocker = "NW_BLOCKER_BLK_" + GetTag(OBJECT_SELF); + SetListenPattern(OBJECT_SELF, sBlocker, i2); +// Determines combat round, if not fighting + SetListenPattern(OBJECT_SELF, CALL_TO_ARMS, i3); + // These call to allies, to move them to a battle. + SetListenPattern(OBJECT_SELF, HELP_MY_FRIEND, i4); + SetListenPattern(OBJECT_SELF, LEADER_FLEE_NOW, i5); + SetListenPattern(OBJECT_SELF, LEADER_ATTACK_TARGET, i6); + // 1.3 - Need a killed one. + SetListenPattern(OBJECT_SELF, I_WAS_KILLED, i7); + // 1.3 - PLaceables/doors which shout this get responded to! + SetListenPattern(OBJECT_SELF, I_WAS_OPENED, i8); +// This will make the listener hear anything, used to react to enemy talking. + SetListenPattern(OBJECT_SELF, "**", i0); +} +// Base for moving round thier waypoints +// - Uses ExectuteScript to run the waypoint walking. +void SpawnWalkWayPoints(int nRun = FALSE, float fPause = 1.0) +{ + SetLocalInt(OBJECT_SELF, WAYPOINT_RUN, nRun); + SetLocalFloat(OBJECT_SELF, WAYPOINT_PAUSE, fPause); + ExecuteScript(FILE_WALK_WAYPOINTS, OBJECT_SELF); +} + + +void AI_SetUpEndOfSpawn() +{ + // Check if we are using custom AI - this cuts out much of the stuff + // used on spawn. + int bCustomAIFile = FALSE; + + // Check custom AI file + if(GetCustomAIFileName() != "") + { + bCustomAIFile = TRUE; + } + + if(GetSpawnInCondition(AI_FLAG_OTHER_RETURN_TO_SPAWN_LOCATION, AI_OTHER_MASTER)) + { + // This will store thier starting location, and then move back there after combat + // Will turn off if there are waypoints. It is set in SetUpEndOfSpawn. + SetLocalLocation(OBJECT_SELF, AI_RETURN_TO_POINT, GetLocation(OBJECT_SELF)); + } + + // Set up if we are immune to cirtain levels of spells naturally for better + // AI spellcasters. + object oHide = GetItemInSlot(INVENTORY_SLOT_CARMOUR, OBJECT_SELF); + // Get if immune is on + if(GetItemHasItemProperty(oHide, ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL)) + { + itemproperty eCheck = GetFirstItemProperty(oHide); + int iAmount; + // Check for item properties until we find the level. + while(GetIsItemPropertyValid(eCheck) && iAmount == FALSE) + { + // Check subtype. + if(GetItemPropertyType(eCheck) == ITEM_PROPERTY_IMMUNITY_SPELLS_BY_LEVEL) + { + // Get the amount + iAmount = GetItemPropertyCostTableValue(eCheck); + } + eCheck = GetNextItemProperty(oHide); + } + // Set it + if(iAmount) + { + SetLocalInt(OBJECT_SELF, AI_SPELL_IMMUNE_LEVEL, iAmount); + } + } + // Animations - any valid? + if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS, NW_GENERIC_MASTER) || + GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN, NW_GENERIC_MASTER) || + GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS, NW_GENERIC_MASTER)) + { + SetAIInteger(AI_VALID_ANIMATIONS, TRUE); + } + + // All things only used by my personal AI. + if(!bCustomAIFile) + { + if(GetLevelByClass(CLASS_TYPE_COMMONER) && GetHitDice(OBJECT_SELF) < i10) + { + SetAIInteger(AI_MORALE, iM1); + } + if(!GetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER)) + { + AI_SetMaybeFearless(); + } + // Set if we are a beholder or mindflayer + switch(GetAppearanceType(OBJECT_SELF)) + { + case 401: //beholder + case 402: //beholder + case 403: //beholder + case 472: // Hive mother + // SetBeholderAI(); + break; + + case 413: //Mindflayer + case 414: // Mindflayer2 + case 415: // Mindflayer_Alhoon + // SetMindflayerAI(); + break; + } + // This NEEDS to be called - to set up categories of spells the creature + // will use + AI_SetUpSpells(); + // Sets up thier selection of skills, to integers, if they would ever use them. + // NOTE: it also triggers "hide" if they have enough skill and not stopped. + AI_SetUpSkillToUse(); + // Sets the turning level if we have FEAT_TURN_UNDEAD. + AI_SetTurningLevel(); + // This sets what weapons the creature will use. They will use the best, according to a "value" + // Giving a creature the feat Two-weapon-fighting makes them deul wield if appropriate weapons. + SetWeapons(); + } + + // We don't set up corpses if set not to...else set to resurrect + // if(!GetSpawnInCondition(AI_FLAG_OTHER_TURN_OFF_CORPSES, AI_OTHER_MASTER)) + { + // Note: Here, if we can, we set Bioware's lootable on. + if(GetSpawnInCondition(AI_FLAG_OTHER_USE_BIOWARE_LOOTING, AI_OTHER_MASTER)) + { + // Set to lootable + SetLootable(OBJECT_SELF, TRUE); + } + // Just handling corpse raising/resurrection/removal + // - Undestroyable, Raiseable and Selectable + SetIsDestroyable(TRUE, TRUE, TRUE); + } + + + // Goes through and sets up which shouts the NPC will listen to. + // - Custom AI uses this also + AI_SetListeningPatterns(); + + // This activates the creatures top aura. + if(GetCommandable()) AI_AdvancedAuras(); +} + +// Sets the turning level if we have FEAT_TURN_UNDEAD. +void AI_SetTurningLevel() +{ + // Most taken directly from NW_S2_TURNDEAD which is used with FEAT_TURN_UNDEAD + if(GetHasFeat(FEAT_TURN_UNDEAD)) + { + // By default, it is clerical levels which provide turn undead power. + // We can turn HD up to nTurnLevel (HD = GetHitDice + GetTurnREsistsnceHD of undead) + int nTurnLevel = GetLevelByClass(CLASS_TYPE_CLERIC); + // Paladins turn at -2 the level of clerics + if(GetLevelByClass(CLASS_TYPE_PALADIN) - i2 > nTurnLevel) + { + nTurnLevel = GetLevelByClass(CLASS_TYPE_PALADIN) - i2; + } + // Blackguard gets to turn at HITDICE -2 level, not character level, + // as it says in NW_S2_TURNDEAD. + if(GetLevelByClass(CLASS_TYPE_BLACKGUARD) > i0 && (GetHitDice(OBJECT_SELF) - i2 > nTurnLevel)) + { + nTurnLevel = GetHitDice(OBJECT_SELF) - i2; + } + // BTW, the number of undead turned is at least nTurnLevel, so we + // can always turn one :-D + SetAIInteger(AI_TURNING_LEVEL, nTurnLevel); + // Note: Turn undead could be used for FEAT_DIVINE_MIGHT and FEAT_DIVINE_SHIELD + } +} + +void AI_SetMaybeFearless() +{ + switch(GetRacialType(OBJECT_SELF)) + { + case RACIAL_TYPE_CONSTRUCT: + case RACIAL_TYPE_DRAGON: + case RACIAL_TYPE_UNDEAD: + case RACIAL_TYPE_OUTSIDER: + { + SetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER); + return; + } + break; + } + if(GetHasFeat(FEAT_AURA_OF_COURAGE) || + GetHasFeat(FEAT_RESIST_NATURES_LURE) || + GetIsImmune(OBJECT_SELF, IMMUNITY_TYPE_FEAR)) + { + SetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER); + } +} + +// Activate all auras. +void AI_ActivateAura(int nAuraNumber) +{ + // Just ActionCast - cheat cast, as then we can use it unlmimted times, as books say. + if(GetHasSpell(nAuraNumber)) + { + ActionCastSpellAtObject(nAuraNumber, OBJECT_SELF, METAMAGIC_NONE, TRUE, i20, PROJECTILE_PATH_TYPE_DEFAULT, TRUE); + } +} + +void AI_AdvancedAuras() +{ + if(GetSpawnInCondition(AI_VALID_TALENT_PERSISTENT_AREA_OF_EFFECT, AI_VALID_SPELLS)) + { + // NOTE: + // - All cheat cast. As DMG, they should be always on OR free action to reapply. + ClearAllActions(); + AI_ActivateAura(SPELLABILITY_DRAGON_FEAR); + AI_ActivateAura(SPELLABILITY_AURA_UNEARTHLY_VISAGE); + AI_ActivateAura(SPELLABILITY_AURA_BLINDING); + AI_ActivateAura(SPELLABILITY_AURA_OF_COURAGE); + AI_ActivateAura(SPELLABILITY_AURA_PROTECTION); + AI_ActivateAura(SPELLABILITY_AURA_STUN); + AI_ActivateAura(SPELLABILITY_AURA_FIRE); + AI_ActivateAura(SPELLABILITY_AURA_COLD); + AI_ActivateAura(SPELLABILITY_AURA_ELECTRICITY); + AI_ActivateAura(SPELLABILITY_AURA_UNNATURAL); + AI_ActivateAura(SPELLABILITY_AURA_FEAR); + AI_ActivateAura(SPELLABILITY_AURA_UNNATURAL); + AI_ActivateAura(SPELLABILITY_AURA_MENACE); + AI_ActivateAura(SPELLABILITY_TYRANT_FOG_MIST); + AI_ActivateAura(AI_SPELLABILITY_AURA_OF_HELLFIRE); + } +} + +// Sets up thier selection of skills, to integers, if they would ever use them. +// NOTE: it also triggers "hide" if they have enough skill and not stopped. +void AI_SetUpSkillToUse() +{ + int iHitDice = GetHitDice(OBJECT_SELF); + // Hiding. We turn off if we have no skill or under 1 skill in it + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_HIDING, AI_OTHER_COMBAT_MASTER)) + { + if(!GetHasSkill(SKILL_HIDE) || + GetSkillRank(SKILL_HIDE) <= i1) + { + SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_HIDING, AI_OTHER_COMBAT_MASTER); + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER); + } + } + // Pickpocketing...we only turn it OFF here if low skill (or none) Needs 1/4 HD. + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PICKPOCKETING, AI_OTHER_COMBAT_MASTER)) + { + if(!GetHasSkill(SKILL_PICK_POCKET) || + GetSkillRank(SKILL_PICK_POCKET) < (iHitDice/i4)) + { + SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PICKPOCKETING, AI_OTHER_COMBAT_MASTER); + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PICKPOCKETING, AI_OTHER_COMBAT_MASTER); + } + } + // Taunting. Again, only off if low skill. Needs half of HD + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_TAUNTING, AI_OTHER_COMBAT_MASTER)) + { + if(!GetHasSkill(SKILL_TAUNT) || + GetSkillRank(SKILL_TAUNT) < (iHitDice/i2)) + { + SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_TAUNTING, AI_OTHER_COMBAT_MASTER); + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_TAUNTING, AI_OTHER_COMBAT_MASTER); + } + } + // Empathy. Again, only off if low skill. Needs any, checked futher in game. + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_EMPATHY, AI_OTHER_COMBAT_MASTER)) + { + if(!GetHasSkill(SKILL_ANIMAL_EMPATHY)) + { + SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_EMPATHY, AI_OTHER_COMBAT_MASTER); + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_EMPATHY, AI_OTHER_COMBAT_MASTER); + } + } + // Unlocking doors. Again, only off if low skill. Needs any, checked futher in game. + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_OPENING_LOCKED_DOORS, AI_OTHER_COMBAT_MASTER)) + { + if(!GetHasSkill(SKILL_OPEN_LOCK)) + { + SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_OPENING_LOCKED_DOORS, AI_OTHER_COMBAT_MASTER); + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_OPENING_LOCKED_DOORS, AI_OTHER_COMBAT_MASTER); + } + } + // Healing kits. + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_USING_HEALING_KITS, AI_OTHER_COMBAT_MASTER)) + { + if(!GetHasSkill(SKILL_HEAL)) + { + SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_USING_HEALING_KITS, AI_OTHER_COMBAT_MASTER); + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_USING_HEALING_KITS, AI_OTHER_COMBAT_MASTER); + } + } + // Parrying + // Only on if we have some skill in it :-) + if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PARRYING, AI_OTHER_COMBAT_MASTER)) + { + if(!GetHasSkill(SKILL_PARRY)) + { + SetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PARRYING, AI_OTHER_COMBAT_MASTER); + DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PARRYING, AI_OTHER_COMBAT_MASTER); + } + } +} + +// This is an attempt to speed up some things. +// We use talents to set general valid categories. +// Levels are also accounted for, checking the spell given and using a switch +// statement to get the level. +void AI_SetUpSpells() +{ + /*************************************************************************** + We use talents, and Get2daString to check levels, and so on... + + We set: + - If the talent is a valid one at all + - If we know it (GetHasSpell) we check the level of the spell. Set if highest. + - We set the actual talent number as a spell. + + // These must match the list in nwscreaturestats.cpp + int TALENT_CATEGORY_HARMFUL_AREAEFFECT_DISCRIMINANT = 1; + int TALENT_CATEGORY_HARMFUL_RANGED = 2; + int TALENT_CATEGORY_HARMFUL_TOUCH = 3; + int TALENT_CATEGORY_BENEFICIAL_HEALING_AREAEFFECT = 4; + int TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH = 5; + int TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT = 6; + int TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE = 7; + int TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT = 8; + int TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE = 9; + int TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF = 10; + int TALENT_CATEGORY_HARMFUL_AREAEFFECT_INDISCRIMINANT = 11; + int TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF = 12; + int TALENT_CATEGORY_BENEFICIAL_PROTECTION_SINGLE = 13; + int TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT = 14; + int TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES = 15; + int TALENT_CATEGORY_PERSISTENT_AREA_OF_EFFECT = 16; + int TALENT_CATEGORY_BENEFICIAL_HEALING_POTION = 17; + int TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_POTION = 18; + int TALENT_CATEGORY_DRAGONS_BREATH = 19; + int TALENT_CATEGORY_BENEFICIAL_PROTECTION_POTION = 20; + int TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_POTION = 21; + int TALENT_CATEGORY_HARMFUL_MELEE = 22; + ***************************************************************************/ + talent tCheck; + // This is set to TRUE if any are valid. :-D + int SpellAnySpell; + + + /** TALENT_CATEGORY_HARMFUL_AREAEFFECT_DISCRIMINANT = 1; ***************** + These are *generally* spells which are Harmful, affect many targets in an + area, and don't hit allies. Spells such as Wierd (An illusion fear spell, so + allies would know it wasn't real, but enemies wouldn't) and so on. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_AREAEFFECT_DISCRIMINANT, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_HARMFUL_AREAEFFECT_DISCRIMINANT, AI_VALID_SPELLS); + } + /** TALENT_CATEGORY_HARMFUL_RANGED = 2; ***************** + These are classed as single target, short or longer ranged spells. Anything + that affects one target, and isn't a touch spell, is this category. Examples + like Acid Arrow or Finger of Death. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_RANGED, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_HARMFUL_RANGED, AI_VALID_SPELLS); + } + /** TALENT_CATEGORY_HARMFUL_TOUCH = 3; ***************** + A limited selection. All touch spells, like Harm. Not much to add but they + only affect one target. Note: Inflict range are also in here (but remember + that GetHasSpell returns TRUE if they can spontaeously cast it as well!) + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_TOUCH, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_HARMFUL_TOUCH, AI_VALID_SPELLS); + } + /** TALENT_CATEGORY_BENEFICIAL_HEALING_AREAEFFECT = 4; ***************** + Healing area effects. Basically, only Mass Heal and Healing Circle :0P + + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_AREAEFFECT, MAXCR); + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_HEALING_AREAEFFECT, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH = 5; ***************** + These are all the healing spells that touch - Cure X wounds and heal really. + Also, feat Wholeness of Body and Lay on Hands as well. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_HEALING_TOUCH, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT = 6; ***************** + These are spells which help people in an area to rid effects, normally. IE + normally, a condition must be met to cast it, like them being stunned. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_AREAEFFECT, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE = 7; ***************** + This is the same as the AOE version, but things like Clarity, single target + ones. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_SINGLE, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT = 8; ***************** + Enhancing stats, or self or others. Friendly, and usually stat-changing. They + contain all ones that, basically, don't protect and stop damage, but help + defeat enemies. In the AOE ones are things like Invisiblity Sphere. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_AREAEFFECT, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE = 9; ***************** + Enchancing, these are the more single ones, like Bulls Strength. See above as well. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SINGLE, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF = 10; **************** + Self-encancing spells, that can't be cast on allies, like Divine Power :-) + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SELF, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_HARMFUL_AREAEFFECT_INDISCRIMINANT = 11; **************** + This is the AOE spells which never discriminate between allies, and enemies, + such as the well known spell Fireball. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_AREAEFFECT_INDISCRIMINANT, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_HARMFUL_AREAEFFECT_INDISCRIMINANT, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF = 12; **************** + Protection spells are usually a Mage's only way to stop instant death. Self + only spells include the likes of Premonition :-) + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_PROTECTION_SELF, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT = 14; **************** + Protection spells are usually a Mage's only way to stop instant death. + Area effect ones are the likes of Protection From Spells. Limited ones here. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_PROTECTION_AREAEFFECT, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES = 15; **************** + Allies, or obtaining them, is anything they can summon. Basically, all + Summon Monster 1-9, Innate ones like Summon Tanarri and ones like Gate. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_OBTAIN_ALLIES, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_PERSISTENT_AREA_OF_EFFECT = 16; **************** + These are NOT AOE spells like acid fog, but rather the Aura's that a monster + can use. Also, oddly, Rage's, Monk's Wholeness of Body and so on are here... + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_PERSISTENT_AREA_OF_EFFECT, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + SpellAnySpell = TRUE; + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_PERSISTENT_AREA_OF_EFFECT, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_DRAGONS_BREATH = 19; **************** + Dragon Breaths. These are all the breaths avalible to Dragons. :-D Nothing else. + + All counted as level 9 innate caster level. Monster ability only :0) + + Contains (By level, innate): + 9. Dragon Breath Acid, Cold, Fear, Fire, Gas, Lightning, Paralyze, Sleep, Slow, Weaken. + ***************************************************************************/ + + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_DRAGONS_BREATH, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck)) + { + // Then set we have it + SetSpawnInCondition(AI_VALID_TALENT_DRAGONS_BREATH, AI_VALID_SPELLS); + } + + /** TALENT_CATEGORY_HARMFUL_MELEE = 22; **************** + We might as well check if we have any valid feats :-P + + Contains: + Called Shot, Disarm, Imp. Power Attack, Knockdown, Power Attack, Rapid Shot, + Sap, Stunning Fist, Flurry of blows, Quivering Palm, Smite Evil, + Expertise (and Imp), Smite Good + Note: + Whirlwind attack (Improved), Dirty Fighting. + ***************************************************************************/ + tCheck = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_MELEE, MAXCR); + // Valid? + if(GetIsTalentValid(tCheck) || + GetHasFeat(FEAT_IMPROVED_WHIRLWIND) || + GetHasFeat(FEAT_WHIRLWIND_ATTACK) || + GetHasFeat(FEAT_DIRTY_FIGHTING) || + GetHasFeat(FEAT_KI_DAMAGE)) + { + // Then set we have it. If we don't have any, we never check Knockdown ETC. + SetSpawnInCondition(AI_VALID_TALENT_HARMFUL_MELEE, AI_VALID_SPELLS); + } + + if(SpellAnySpell == TRUE) + { + SetSpawnInCondition(AI_VALID_ANY_SPELL, AI_VALID_SPELLS); + } + // All spells in no category. + if( +// GetHasSpell(SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE) || + GetHasSpell(SPELL_DARKNESS) || +// GetHasSpell(71) || // Greater shadow conjuration +// GetHasSpell(SPELL_IDENTIFY) || +// GetHasSpell(SPELL_KNOCK) || + GetHasSpell(SPELL_LIGHT) || + GetHasSpell(SPELL_POLYMORPH_SELF) || +// GetHasSpell(158) || // Shades +// GetHasSpell(159) || // Shadow conjuration + GetHasSpell(SPELL_SHAPECHANGE) || + GetHasSpell(321) || // Protection... + GetHasSpell(322) || // Magic circle... + GetHasSpell(323) || // Aura... +// GetHasSpell(SPELL_LEGEND_LORE) || +// GetHasSpell(SPELL_FIND_TRAPS) || + GetHasSpell(SPELL_CONTINUAL_FLAME) || +// GetHasSpell(SPELL_ONE_WITH_THE_LAND) || +// GetHasSpell(SPELL_CAMOFLAGE) || + GetHasSpell(SPELL_BLOOD_FRENZY) || +// GetHasSpell(SPELL_AMPLIFY) || + GetHasSpell(SPELL_ETHEREALNESS) || + GetHasSpell(SPELL_DIVINE_SHIELD) || + GetHasSpell(SPELL_DIVINE_MIGHT) || + // Added in again 18th nov + GetHasSpell(SPELL_INFLICT_SERIOUS_WOUNDS) || + GetHasSpell(SPELL_INFLICT_MODERATE_WOUNDS) || + GetHasSpell(SPELL_INFLICT_MINOR_WOUNDS) || + GetHasSpell(SPELL_INFLICT_LIGHT_WOUNDS) || + // Feats that will be cast in the spell list. + // MOST are under talents anyway, these are ones which are not + GetHasFeat(FEAT_PRESTIGE_DARKNESS)) + { + SetSpawnInCondition(AI_VALID_OTHER_SPELL, AI_VALID_SPELLS); + SetSpawnInCondition(AI_VALID_ANY_SPELL, AI_VALID_SPELLS); + } + // SPELL_GREATER_RESTORATION - Ability Decrease, AC decrease, Attack decrease, + // Damage Decrease, Damage Immunity Decrease, Saving Throw Decrease, Spell + // resistance Decrease, Skill decrease, Blindness, Deaf, Curse, Disease, Poison, + // Charmed, Dominated, Dazed, Confused, Frightened, Negative level, Paralyze, + // Slow, Stunned. + // SPELL_FREEDOM - Paralyze, Entangle, Slow, Movement speed decrease. (+Immunity!) + // SPELL_RESTORATION - Ability Decrease, AC decrease, Attack Decrease, + // Damage Decrease, Damage Immunity Decrease, Saving Throw Decrease, + // Spell Resistance Decrease, Skill Decrease, Blindess, Deaf, Paralyze, Negative level + // SPELL_REMOVE_BLINDNESS_AND_DEAFNESS - Blindess, Deaf. + // SPELL_NEUTRALIZE_POISON - Poison + // SPELL_REMOVE_DISEASE - Disease + // SPELL_REMOVE_CURSE - Curse + // SPELL_LESSER_RESTORATION - Ability Decrease, AC decrease, Attack Decrease, + // Cure condition spells! :-) + if(GetHasSpell(SPELL_GREATER_RESTORATION) || GetHasSpell(SPELL_FREEDOM_OF_MOVEMENT) || + GetHasSpell(SPELL_RESTORATION) || GetHasSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS) || + GetHasSpell(SPELL_NEUTRALIZE_POISON) || GetHasSpell(SPELL_REMOVE_DISEASE) || + GetHasSpell(SPELL_REMOVE_CURSE) || GetHasSpell(SPELL_LESSER_RESTORATION) || + GetHasSpell(SPELL_STONE_TO_FLESH)) + { + SetSpawnInCondition(AI_VALID_CURE_CONDITION_SPELLS, AI_VALID_SPELLS); + } +} + +// This will make the visual effect passed play INSTANTLY at the creatures location. +// * iVFX - The visual effect constant number +void AI_SpawnInInstantVisual(int iVFX) +{ + // Beta 3 change: Made to at location. + ApplyEffectAtLocation(DURATION_TYPE_INSTANT, + EffectVisualEffect(iVFX), + GetLocation(OBJECT_SELF)); +} +// This will make the visual effect passed play PERMAMENTLY. +// * If a VFX is -1, it is ignored. +// * iVFX1-4 - The visual effect constant number +// NOTE: They will be made to be SUPERNATUAL, so are not dispelled! +void AI_SpawnInPermamentVisual(int iVFX) +{ + ApplyEffectToObject(DURATION_TYPE_INSTANT, + SupernaturalEffect(EffectVisualEffect(iVFX)), + OBJECT_SELF); +} +// This will set the MAXIMUM and MINIMUM targets to pass this stage (under sName) +// of the targeting system. IE: +// - If we set it to min of 5, max of 10, for AC, if we cancled 5 targets on having +// AC higher then wanted, we will take the highest 5 minimum. +// If there are over 10, we take a max of 10 to choose from. +// * iType - must be TARGET_HIGHER or TARGET_LOWER. Defaults to the lowest, so it +// targets the lowest value for sName. +void AI_SetAITargetingValues(string sName, int iType, int iMinimum, int iMaximum) +{ + // Error checking + if((iType == TARGET_HIGHER || iType == TARGET_LOWER) && + iMinimum >= i1 && iMaximum >= i1 && iMaximum >= iMinimum) + { + // Set the type to sName. + // - It is TARGET_HIGHER (1) or TARGET_LOWER (0). + SetAIInteger(sName, iType); + + // Minimum amount of targets for this? + SetAIInteger(sName + MINIMUM, iMinimum); + + // Maximum targets for this? + SetAIInteger(sName + MAXIMUM, iMaximum); + } +} + +// Sets we are a Beholder and use Ray attacks, and Animagic Ray. +void SetBeholderAI() +{ + // 1 = beholder + SetAIInteger(AI_SPECIAL_AI, i1); +} +// Set we are a mindflayer, and uses some special AI for them. +void SetMindflayerAI() +{ + // 2 = mindflayer + SetAIInteger(AI_SPECIAL_AI, i2); +} + +// This will set a spell trigger up. Under cirtain conditions, spells are released +// and cast on the caster. +// Once fired, a spell trigger is only reset by resting. +// * sType - is specifically: +// SPELLTRIGGER_DAMAGED_AT_PERCENT - When damaged, the trigger fires. Use iValue for the %. One at a time is fired. +// SPELLTRIGGER_IMMOBILE - Fired when held/paralyzed/sleeping ETC. One at a time is fired. +// SPELLTRIGGER_NOT_GOT_FIRST_SPELL - Makes sure !GetHasSpellEffect(iSpell1) already, +// then fires. Checks all in this category each round. +// SPELLTRIGGER_START_OF_COMBAT - All these are all fired at the start of combat. +// * iNumber - can be 1-9, in sequential order of when you want them to fire. +// * iValue - is only required with DAMAGED_AT_PERCENT. Only the first one set is used. +// * iSpellX - Cannot be 0. It should only really be defensive spells. +void SetSpellTrigger(string sType, int iValue, int iNumber, int iSpell1, int iSpell2 = 0, int iSpell3 = 0, int iSpell4 = 0, int iSpell5 = 0, int iSpell6 = 0, int iSpell7 = 0, int iSpell8 = 0, int iSpell9 = 0) +{ + // Either get our spell trigger (creature) or create one + object oTrigger = GetAIObject(AI_SPELL_TRIGGER_CREATURE); + // Is it valid? + if(!GetIsObjectValid(oTrigger)) + { + // Create it + oTrigger = CreateObject(OBJECT_TYPE_CREATURE, "jass_spelltrig", GetLocation(OBJECT_SELF)); + // Set local on them for the target + AddHenchman(OBJECT_SELF, oTrigger); + // Local for us to get our spell trigger (should be our henchmen) + SetAIObject(AI_SPELL_TRIGGER_CREATURE, oTrigger); + } + int iSize = i1; + string sTotalID = sType + IntToString(iNumber); + + SetLocalInt(oTrigger, sTotalID + "1", iSpell1); + if(iSpell2 != FALSE) + { iSize = 2; + SetLocalInt(oTrigger, sTotalID + "2", iSpell2); } + if(iSpell3 != FALSE) + { iSize = 3; + SetLocalInt(oTrigger, sTotalID + "3", iSpell3); } + if(iSpell4 != FALSE) + { iSize = 4; + SetLocalInt(oTrigger, sTotalID + "4", iSpell4); } + if(iSpell5 != FALSE) + { iSize = 5; + SetLocalInt(oTrigger, sTotalID + "5", iSpell5); } + if(iSpell6 != FALSE) + { iSize = 6; + SetLocalInt(oTrigger, sTotalID + "6", iSpell6); } + if(iSpell7 != FALSE) + { iSize = 7; + SetLocalInt(oTrigger, sTotalID + "7", iSpell7); } + if(iSpell8 != FALSE) + { iSize = 8; + SetLocalInt(oTrigger, sTotalID + "8", iSpell8); } + if(iSpell9 != FALSE) + { iSize = 9; + SetLocalInt(oTrigger, sTotalID + "9", iSpell9); } + + // Set final sizes ETC. + SetLocalInt(oTrigger, MAXINT_ + sTotalID, iSize); + SetLocalInt(oTrigger, sTotalID + USED, FALSE); + + // Check value + if(iValue > GetLocalInt(oTrigger, VALUE + sType)) + { + SetLocalInt(oTrigger, VALUE + sType, iValue); + } + + // Set how many spell triggers we have too + if(iNumber > GetLocalInt(oTrigger, MAXIMUM + sType)) + { + SetLocalInt(oTrigger, MAXIMUM + sType, iNumber); + } +} +// Debug: To compile this script, uncomment all of the below. +// void main() {} diff --git a/_content/_hak/rune_prc8_top/nw_c2_default7.ncs b/_content/_hak/rune_prc8_top/nw_c2_default7.ncs index 7d0674d..0c4ec9d 100644 Binary files a/_content/_hak/rune_prc8_top/nw_c2_default7.ncs and b/_content/_hak/rune_prc8_top/nw_c2_default7.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_c2_default9.ncs b/_content/_hak/rune_prc8_top/nw_c2_default9.ncs index 313c420..01acc23 100644 Binary files a/_content/_hak/rune_prc8_top/nw_c2_default9.ncs and b/_content/_hak/rune_prc8_top/nw_c2_default9.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_c2_defaultd.ncs b/_content/_hak/rune_prc8_top/nw_c2_defaultd.ncs index 13a97ac..2df68be 100644 Binary files a/_content/_hak/rune_prc8_top/nw_c2_defaultd.ncs and b/_content/_hak/rune_prc8_top/nw_c2_defaultd.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurablnda.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurablnda.ncs index 69a359e..e4aa3c2 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_aurablnda.ncs and b/_content/_hak/rune_prc8_top/nw_s1_aurablnda.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_auracoldc.ncs b/_content/_hak/rune_prc8_top/nw_s1_auracoldc.ncs index 25e81a4..a5e7712 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_auracoldc.ncs and b/_content/_hak/rune_prc8_top/nw_s1_auracoldc.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_auraelecc.ncs b/_content/_hak/rune_prc8_top/nw_s1_auraelecc.ncs index 7f66cf4..2d8e4c0 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_auraelecc.ncs and b/_content/_hak/rune_prc8_top/nw_s1_auraelecc.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurafirec.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurafirec.ncs index 7e37f9b..725e169 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_aurafirec.ncs and b/_content/_hak/rune_prc8_top/nw_s1_aurafirec.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_auramenca.ncs b/_content/_hak/rune_prc8_top/nw_s1_auramenca.ncs index 864f53e..507f037 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_auramenca.ncs and b/_content/_hak/rune_prc8_top/nw_s1_auramenca.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurastuna.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurastuna.ncs index 93234a4..b6eda58 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_aurastuna.ncs and b/_content/_hak/rune_prc8_top/nw_s1_aurastuna.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurauneaa.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurauneaa.ncs index fc49bf0..34bc167 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_aurauneaa.ncs and b/_content/_hak/rune_prc8_top/nw_s1_aurauneaa.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltacid.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltacid.ncs index fcf323e..a5952b3 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltacid.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltacid.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltcharm.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltcharm.ncs index fa6e617..2a37c20 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltcharm.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltcharm.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltchrdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltchrdr.ncs index 8f99f48..1d69902 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltchrdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltchrdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltcold.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltcold.ncs index 09b19ff..2d00a86 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltcold.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltcold.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltcondr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltcondr.ncs index 5320de0..6560555 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltcondr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltcondr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltconf.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltconf.ncs index 8b56a10..e74ecf4 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltconf.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltconf.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdaze.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdaze.ncs index 5ae5e30..b8ec5d2 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltdaze.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltdaze.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdeath.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdeath.ncs index b66c290..ef10b59 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltdeath.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltdeath.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdexdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdexdr.ncs index 863e85b..2904c69 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltdexdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltdexdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdisese.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdisese.ncs index d90e269..c81e0cc 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltdisese.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltdisese.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdomn.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdomn.ncs index 063a389..3c354b3 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltdomn.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltdomn.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltfire.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltfire.ncs index dd3f0df..09ac47c 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltfire.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltfire.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltintdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltintdr.ncs index 732cb9d..748e670 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltintdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltintdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltknckd.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltknckd.ncs index 398ff64..e9a8d53 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltknckd.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltknckd.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltlightn.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltlightn.ncs index 7971f25..0286bec 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltlightn.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltlightn.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltlvldr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltlvldr.ncs index 3dc34d7..e8628c5 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltlvldr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltlvldr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltparal.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltparal.ncs index 315054f..95cce17 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltparal.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltparal.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltpoison.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltpoison.ncs index f4a5a40..e4066f7 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltpoison.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltpoison.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltshards.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltshards.ncs index f2b6876..00a5432 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltshards.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltshards.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltslow.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltslow.ncs index ab2bf8f..d59eaa3 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltslow.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltslow.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltstrdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltstrdr.ncs index 4406f76..7819b3e 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltstrdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltstrdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltstun.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltstun.ncs index b37d887..7a8f0d3 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltstun.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltstun.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltweb.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltweb.ncs index 2905142..67c3671 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltweb.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltweb.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltwisdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltwisdr.ncs index cb60321..93d66dc 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_bltwisdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_bltwisdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_coneacid.ncs b/_content/_hak/rune_prc8_top/nw_s1_coneacid.ncs index 763346a..fc71423 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_coneacid.ncs and b/_content/_hak/rune_prc8_top/nw_s1_coneacid.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_conecold.ncs b/_content/_hak/rune_prc8_top/nw_s1_conecold.ncs index fa1c7b2..c391d7c 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_conecold.ncs and b/_content/_hak/rune_prc8_top/nw_s1_conecold.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_coneelec.ncs b/_content/_hak/rune_prc8_top/nw_s1_coneelec.ncs index 1f2cad0..d3cd823 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_coneelec.ncs and b/_content/_hak/rune_prc8_top/nw_s1_coneelec.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_conesonic.ncs b/_content/_hak/rune_prc8_top/nw_s1_conesonic.ncs index 4769d6a..010f94f 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_conesonic.ncs and b/_content/_hak/rune_prc8_top/nw_s1_conesonic.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_dragfear.ncs b/_content/_hak/rune_prc8_top/nw_s1_dragfear.ncs index 53d660d..fb1ce16 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_dragfear.ncs and b/_content/_hak/rune_prc8_top/nw_s1_dragfear.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_dragfeara.ncs b/_content/_hak/rune_prc8_top/nw_s1_dragfeara.ncs index 73206fb..fe3245d 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_dragfeara.ncs and b/_content/_hak/rune_prc8_top/nw_s1_dragfeara.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazechaos.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazechaos.ncs index d26aae3..5c42800 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazechaos.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazechaos.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazecharm.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazecharm.ncs index fafca7d..af690f7 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazecharm.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazecharm.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazeconfu.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazeconfu.ncs index b06cabc..d632bfa 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazeconfu.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazeconfu.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazedaze.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazedaze.ncs index b01ab3e..db48941 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazedaze.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazedaze.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazedeath.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazedeath.ncs index cf1b2f8..0930ec8 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazedeath.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazedeath.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazedomn.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazedomn.ncs index b390643..ac24c75 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazedomn.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazedomn.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazedoom.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazedoom.ncs index c6a8096..a20712c 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazedoom.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazedoom.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazeevil.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazeevil.ncs index 9df6b98..3b1140c 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazeevil.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazeevil.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazefear.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazefear.ncs index fd1e390..e543fbb 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazefear.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazefear.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazegood.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazegood.ncs index 5a45f9e..4f0ea5e 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazegood.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazegood.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazelaw.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazelaw.ncs index d492409..ddf8f30 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazelaw.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazelaw.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazestun.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazestun.ncs index 0ffd54d..9330b14 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_gazestun.ncs and b/_content/_hak/rune_prc8_top/nw_s1_gazestun.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_hndbreath.ncs b/_content/_hak/rune_prc8_top/nw_s1_hndbreath.ncs index 5b02d9f..2a2d1cf 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_hndbreath.ncs and b/_content/_hak/rune_prc8_top/nw_s1_hndbreath.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlconf.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlconf.ncs index 09a038a..2649dad 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_howlconf.ncs and b/_content/_hak/rune_prc8_top/nw_s1_howlconf.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_howldaze.ncs b/_content/_hak/rune_prc8_top/nw_s1_howldaze.ncs index 606f8bd..fc595ff 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_howldaze.ncs and b/_content/_hak/rune_prc8_top/nw_s1_howldaze.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_howldeath.ncs b/_content/_hak/rune_prc8_top/nw_s1_howldeath.ncs index 1e7e120..c1f8a98 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_howldeath.ncs and b/_content/_hak/rune_prc8_top/nw_s1_howldeath.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlfear.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlfear.ncs index 1878ff2..1ac9f0e 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_howlfear.ncs and b/_content/_hak/rune_prc8_top/nw_s1_howlfear.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlparal.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlparal.ncs index b3899ae..42488a9 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_howlparal.ncs and b/_content/_hak/rune_prc8_top/nw_s1_howlparal.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlsonic.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlsonic.ncs index 5ea39cc..ae721fd 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_howlsonic.ncs and b/_content/_hak/rune_prc8_top/nw_s1_howlsonic.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlstun.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlstun.ncs index 4505a09..9a8d7f6 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_howlstun.ncs and b/_content/_hak/rune_prc8_top/nw_s1_howlstun.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_krenscare.ncs b/_content/_hak/rune_prc8_top/nw_s1_krenscare.ncs index a11930a..c2c9876 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_krenscare.ncs and b/_content/_hak/rune_prc8_top/nw_s1_krenscare.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_mephsalt.ncs b/_content/_hak/rune_prc8_top/nw_s1_mephsalt.ncs index ab0b266..5406ee0 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_mephsalt.ncs and b/_content/_hak/rune_prc8_top/nw_s1_mephsalt.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_mephsteam.ncs b/_content/_hak/rune_prc8_top/nw_s1_mephsteam.ncs index ba8664b..f61d7ef 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_mephsteam.ncs and b/_content/_hak/rune_prc8_top/nw_s1_mephsteam.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulschrdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulschrdr.ncs index 1f7a0d2..acc6c11 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulschrdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulschrdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulscold.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulscold.ncs index de7893f..23df913 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulscold.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulscold.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulscondr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulscondr.ncs index 4f364f0..39b62d7 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulscondr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulscondr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsdeath.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsdeath.ncs index 5f8049a..6879326 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulsdeath.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulsdeath.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsdexdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsdexdr.ncs index c042b11..60b1f47 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulsdexdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulsdexdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulselec.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulselec.ncs index e8c36f9..dd6a5f0 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulselec.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulselec.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsfire.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsfire.ncs index dd6247c..1da13e5 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulsfire.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulsfire.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsholy.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsholy.ncs index 91f2d46..3b93769 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulsholy.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulsholy.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsintdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsintdr.ncs index 44f629c..ddf504a 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulsintdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulsintdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulslvldr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulslvldr.ncs index ba715d4..408bf6e 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulslvldr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulslvldr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsneg.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsneg.ncs index 34c6acd..7dc823b 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulsneg.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulsneg.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsstrdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsstrdr.ncs index 6302414..704c1ca 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulsstrdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulsstrdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulswind.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulswind.ncs index 09e520a..75d4133 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulswind.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulswind.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulswisdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulswisdr.ncs index 1b33c9a..da829ca 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_pulswisdr.ncs and b/_content/_hak/rune_prc8_top/nw_s1_pulswisdr.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_smokeclaw.ncs b/_content/_hak/rune_prc8_top/nw_s1_smokeclaw.ncs index 85ee73d..2e5a547 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_smokeclaw.ncs and b/_content/_hak/rune_prc8_top/nw_s1_smokeclaw.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_stink_a.ncs b/_content/_hak/rune_prc8_top/nw_s1_stink_a.ncs index 30142be..bca10c2 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_stink_a.ncs and b/_content/_hak/rune_prc8_top/nw_s1_stink_a.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s1_tyrantfga.ncs b/_content/_hak/rune_prc8_top/nw_s1_tyrantfga.ncs index 5ac4b25..5f27b26 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s1_tyrantfga.ncs and b/_content/_hak/rune_prc8_top/nw_s1_tyrantfga.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s2_divprot.ncs b/_content/_hak/rune_prc8_top/nw_s2_divprot.ncs index b28c6e6..7f7632c 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s2_divprot.ncs and b/_content/_hak/rune_prc8_top/nw_s2_divprot.ncs differ diff --git a/_content/_hak/rune_prc8_top/nw_s3_balordeth.ncs b/_content/_hak/rune_prc8_top/nw_s3_balordeth.ncs index 6583786..4e9dd36 100644 Binary files a/_content/_hak/rune_prc8_top/nw_s3_balordeth.ncs and b/_content/_hak/rune_prc8_top/nw_s3_balordeth.ncs differ diff --git a/_release/Rune [PRC8-CEP2].7z b/_release/Rune [PRC8-CEP2].7z deleted file mode 100644 index eddf824..0000000 Binary files a/_release/Rune [PRC8-CEP2].7z and /dev/null differ