From e1170da304364b44d7c386bf568fe2b254fb27e2 Mon Sep 17 00:00:00 2001 From: Jaysyn904 <68194417+Jaysyn904@users.noreply.github.com> Date: Thu, 3 Apr 2025 21:32:18 -0400 Subject: [PATCH] Removed _release archive Removed _release archive. Updated top hak. --- _content/Compiler - Top Hak.bat | 4 + _content/_hak/rune_prc8_top/dmfi_dmw_inc.nss | 611 + _content/_hak/rune_prc8_top/j_inc_basic.nss | 582 + .../_hak/rune_prc8_top/j_inc_constants.nss | 2189 +++ _content/_hak/rune_prc8_top/j_inc_debug.nss | 188 + .../_hak/rune_prc8_top/j_inc_generic_ai.nss | 14964 ++++++++++++++++ .../_hak/rune_prc8_top/j_inc_heartbeat.nss | 277 + .../_hak/rune_prc8_top/j_inc_npc_attack.nss | 432 + .../_hak/rune_prc8_top/j_inc_other_ai.nss | 511 + .../_hak/rune_prc8_top/j_inc_seteffects.nss | 444 + .../_hak/rune_prc8_top/j_inc_setweapons.nss | 2043 +++ _content/_hak/rune_prc8_top/j_inc_spawnin.nss | 1237 ++ .../_hak/rune_prc8_top/nw_c2_default7.ncs | Bin 1309 -> 1533 bytes .../_hak/rune_prc8_top/nw_c2_default9.ncs | Bin 189837 -> 189655 bytes .../_hak/rune_prc8_top/nw_c2_defaultd.ncs | Bin 1071 -> 986 bytes .../_hak/rune_prc8_top/nw_s1_aurablnda.ncs | Bin 39974 -> 40311 bytes .../_hak/rune_prc8_top/nw_s1_auracoldc.ncs | Bin 39973 -> 40310 bytes .../_hak/rune_prc8_top/nw_s1_auraelecc.ncs | Bin 40010 -> 40347 bytes .../_hak/rune_prc8_top/nw_s1_aurafirec.ncs | Bin 39959 -> 40296 bytes .../_hak/rune_prc8_top/nw_s1_auramenca.ncs | Bin 52684 -> 53021 bytes .../_hak/rune_prc8_top/nw_s1_aurastuna.ncs | Bin 40235 -> 40572 bytes .../_hak/rune_prc8_top/nw_s1_aurauneaa.ncs | Bin 39733 -> 40070 bytes _content/_hak/rune_prc8_top/nw_s1_bltacid.ncs | Bin 46016 -> 46359 bytes .../_hak/rune_prc8_top/nw_s1_bltcharm.ncs | Bin 46420 -> 46763 bytes .../_hak/rune_prc8_top/nw_s1_bltchrdr.ncs | Bin 45240 -> 45583 bytes _content/_hak/rune_prc8_top/nw_s1_bltcold.ncs | Bin 45964 -> 46307 bytes .../_hak/rune_prc8_top/nw_s1_bltcondr.ncs | Bin 45242 -> 45585 bytes _content/_hak/rune_prc8_top/nw_s1_bltconf.ncs | Bin 45722 -> 46065 bytes _content/_hak/rune_prc8_top/nw_s1_bltdaze.ncs | Bin 46422 -> 46765 bytes .../_hak/rune_prc8_top/nw_s1_bltdeath.ncs | Bin 45060 -> 45403 bytes .../_hak/rune_prc8_top/nw_s1_bltdexdr.ncs | Bin 45250 -> 45593 bytes .../_hak/rune_prc8_top/nw_s1_bltdisese.ncs | Bin 8953 -> 8959 bytes _content/_hak/rune_prc8_top/nw_s1_bltdomn.ncs | Bin 46610 -> 46953 bytes _content/_hak/rune_prc8_top/nw_s1_bltfire.ncs | Bin 45964 -> 46307 bytes .../_hak/rune_prc8_top/nw_s1_bltintdr.ncs | Bin 45250 -> 45593 bytes .../_hak/rune_prc8_top/nw_s1_bltknckd.ncs | Bin 45247 -> 45590 bytes .../_hak/rune_prc8_top/nw_s1_bltlightn.ncs | Bin 46082 -> 46425 bytes .../_hak/rune_prc8_top/nw_s1_bltlvldr.ncs | Bin 45233 -> 45576 bytes .../_hak/rune_prc8_top/nw_s1_bltparal.ncs | Bin 45656 -> 45999 bytes .../_hak/rune_prc8_top/nw_s1_bltpoison.ncs | Bin 9861 -> 9867 bytes .../_hak/rune_prc8_top/nw_s1_bltshards.ncs | Bin 45862 -> 46205 bytes _content/_hak/rune_prc8_top/nw_s1_bltslow.ncs | Bin 45292 -> 45635 bytes .../_hak/rune_prc8_top/nw_s1_bltstrdr.ncs | Bin 45248 -> 45591 bytes _content/_hak/rune_prc8_top/nw_s1_bltstun.ncs | Bin 46491 -> 46834 bytes _content/_hak/rune_prc8_top/nw_s1_bltweb.ncs | Bin 45184 -> 45527 bytes .../_hak/rune_prc8_top/nw_s1_bltwisdr.ncs | Bin 45235 -> 45578 bytes .../_hak/rune_prc8_top/nw_s1_coneacid.ncs | Bin 42826 -> 43163 bytes .../_hak/rune_prc8_top/nw_s1_conecold.ncs | Bin 42826 -> 43163 bytes .../_hak/rune_prc8_top/nw_s1_coneelec.ncs | Bin 42979 -> 43316 bytes .../_hak/rune_prc8_top/nw_s1_conesonic.ncs | Bin 42820 -> 43157 bytes .../_hak/rune_prc8_top/nw_s1_dragfear.ncs | Bin 43648 -> 43985 bytes .../_hak/rune_prc8_top/nw_s1_dragfeara.ncs | Bin 40292 -> 40629 bytes .../_hak/rune_prc8_top/nw_s1_gazechaos.ncs | Bin 42185 -> 42522 bytes .../_hak/rune_prc8_top/nw_s1_gazecharm.ncs | Bin 43689 -> 44026 bytes .../_hak/rune_prc8_top/nw_s1_gazeconfu.ncs | Bin 43715 -> 44052 bytes .../_hak/rune_prc8_top/nw_s1_gazedaze.ncs | Bin 42883 -> 43220 bytes .../_hak/rune_prc8_top/nw_s1_gazedeath.ncs | Bin 42222 -> 42559 bytes .../_hak/rune_prc8_top/nw_s1_gazedomn.ncs | Bin 43716 -> 44053 bytes .../_hak/rune_prc8_top/nw_s1_gazedoom.ncs | Bin 42552 -> 42889 bytes .../_hak/rune_prc8_top/nw_s1_gazeevil.ncs | Bin 42225 -> 42562 bytes .../_hak/rune_prc8_top/nw_s1_gazefear.ncs | Bin 42921 -> 43258 bytes .../_hak/rune_prc8_top/nw_s1_gazegood.ncs | Bin 42225 -> 42562 bytes _content/_hak/rune_prc8_top/nw_s1_gazelaw.ncs | Bin 42267 -> 42604 bytes .../_hak/rune_prc8_top/nw_s1_gazestun.ncs | Bin 43649 -> 43986 bytes .../_hak/rune_prc8_top/nw_s1_hndbreath.ncs | Bin 42646 -> 42983 bytes .../_hak/rune_prc8_top/nw_s1_howlconf.ncs | Bin 40858 -> 41201 bytes .../_hak/rune_prc8_top/nw_s1_howldaze.ncs | Bin 40858 -> 41201 bytes .../_hak/rune_prc8_top/nw_s1_howldeath.ncs | Bin 40133 -> 40476 bytes .../_hak/rune_prc8_top/nw_s1_howlfear.ncs | Bin 40854 -> 41197 bytes .../_hak/rune_prc8_top/nw_s1_howlparal.ncs | Bin 40719 -> 41062 bytes .../_hak/rune_prc8_top/nw_s1_howlsonic.ncs | Bin 40299 -> 40642 bytes .../_hak/rune_prc8_top/nw_s1_howlstun.ncs | Bin 40812 -> 41155 bytes .../_hak/rune_prc8_top/nw_s1_krenscare.ncs | Bin 54125 -> 54468 bytes .../_hak/rune_prc8_top/nw_s1_mephsalt.ncs | Bin 46171 -> 46514 bytes .../_hak/rune_prc8_top/nw_s1_mephsteam.ncs | Bin 46171 -> 46514 bytes .../_hak/rune_prc8_top/nw_s1_pulschrdr.ncs | Bin 40261 -> 40598 bytes .../_hak/rune_prc8_top/nw_s1_pulscold.ncs | Bin 40867 -> 41204 bytes .../_hak/rune_prc8_top/nw_s1_pulscondr.ncs | Bin 40261 -> 40598 bytes .../_hak/rune_prc8_top/nw_s1_pulsdeath.ncs | Bin 40166 -> 40503 bytes .../_hak/rune_prc8_top/nw_s1_pulsdexdr.ncs | Bin 40261 -> 40598 bytes .../_hak/rune_prc8_top/nw_s1_pulselec.ncs | Bin 41052 -> 41389 bytes .../_hak/rune_prc8_top/nw_s1_pulsfire.ncs | Bin 40855 -> 41192 bytes .../_hak/rune_prc8_top/nw_s1_pulsholy.ncs | Bin 41198 -> 41535 bytes .../_hak/rune_prc8_top/nw_s1_pulsintdr.ncs | Bin 40259 -> 40596 bytes .../_hak/rune_prc8_top/nw_s1_pulslvldr.ncs | Bin 40235 -> 40572 bytes _content/_hak/rune_prc8_top/nw_s1_pulsneg.ncs | Bin 41262 -> 41599 bytes .../_hak/rune_prc8_top/nw_s1_pulsstrdr.ncs | Bin 40261 -> 40598 bytes .../_hak/rune_prc8_top/nw_s1_pulswind.ncs | Bin 39899 -> 40236 bytes .../_hak/rune_prc8_top/nw_s1_pulswisdr.ncs | Bin 40263 -> 40600 bytes .../_hak/rune_prc8_top/nw_s1_smokeclaw.ncs | Bin 45289 -> 45632 bytes _content/_hak/rune_prc8_top/nw_s1_stink_a.ncs | Bin 40382 -> 40719 bytes .../_hak/rune_prc8_top/nw_s1_tyrantfga.ncs | Bin 39875 -> 40212 bytes _content/_hak/rune_prc8_top/nw_s2_divprot.ncs | Bin 57272 -> 57272 bytes .../_hak/rune_prc8_top/nw_s3_balordeth.ncs | Bin 198679 -> 199288 bytes _release/Rune [PRC8-CEP2].7z | Bin 7452620 -> 0 bytes 95 files changed, 23482 insertions(+) create mode 100644 _content/Compiler - Top Hak.bat create mode 100644 _content/_hak/rune_prc8_top/dmfi_dmw_inc.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_basic.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_constants.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_debug.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_generic_ai.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_heartbeat.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_npc_attack.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_other_ai.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_seteffects.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_setweapons.nss create mode 100644 _content/_hak/rune_prc8_top/j_inc_spawnin.nss delete mode 100644 _release/Rune [PRC8-CEP2].7z 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 7d0674daed33ecd230f62aa39ce42773ea2ab7f0..0c4ec9d3bf74f481155f177999a15041a13a13f6 100644 GIT binary patch delta 437 zcmbQs^_QF1&pB8j%uvt3iGhLj??hgoKqh7uHU=PIVFnT+tPG6dOpJ{G|NsBNz#`26 zr2oJaure^zGonec0o8A1Koy)=^^$uASnC%B1}5goIgAQ?s46D+Fm9-4u4Q6_*o-R4 z#LNt}o|S>g5#2&o7FGtnfSknS)WoEm)OeSo`~o!XAQij_6@I0;XbPCYK2=3?HPDx! z06?>Eas$&x0VY5U?-<2@zHX#&9Nx04oDSJtL66m0{wl zmz*8a4FCWC|DwP!c^;#}rxh IHf22l0P5X2XaE2J diff --git a/_content/_hak/rune_prc8_top/nw_c2_default9.ncs b/_content/_hak/rune_prc8_top/nw_c2_default9.ncs index 313c42071c500a784315bc47c0ce38fe1fbabea4..01acc232edd2b8b51ac85ef58f2a39f0dc121ecf 100644 GIT binary patch delta 980 zcmYk4Ye-aE6vy}aJBl-N#!B-{Sso*95CxWL@0Vl=NlsDFr6|zUQnS>Yw1<^spN7(G zS4*_iD7%nx!3H~@x^;t~=wVS&R0LTOLC}LPT*|b4_US`k)>{AH-kZf*|9#}P|H&<{NpKLt%R;5A{T)ymtl8s+5+Gy;Q~R`S?N=+!R8l zze>$PYRa#|!w))<%%Mw&x8H~GFHwcvvkEmMX$hwlB9}7l&4t)Nz+?Wg3F%(717`|! z@b^usTM1_sVIJ+^$|4*b@z=zdG<7HpJ?Nv8#n=^Gq&p1?kFz@J@K2cJ+?dSG#Ymo= zsCVl|L)US7{e!y4N+$-cJ2CN{C(e22)JdOsaUranY*z}_J%idBa<9cIcj1=5u;UxK zb*!KqZAEqx0>b8q3kq5M{y#2Kfe-fGxbN+5DpiVoo z8xbD{lGr?iUb<%AJcPcDfgbmxIG$6H9T`xA7}_4;QRYpV!dG8o&e(%v^|jl-St(+L L@WS@JK_mSH3EUXf delta 1147 zcmZvae{54#6vyv5uU#EoyGiX6WPK|l^2f%64AGDxE2T3VbDQI52xJa$Y~7D+rHm?M zWj4eg89011hk$I={b7PegU#}SX8vIYN;C|!Ec}Be%P=7klrEXkSW?e@Z^W4R$2;eK zKj++g&pY?ryyiUhz0*|}+UDQ6wtUTIfxn!(63e+=~i__!ys2wKsNyjqyBmJx6oT6t`RX^|4^pj3i zpJacTy;7j*_nd0_xq@PVz4~_73AWR&9=821pMK*DyI$sYr;oaSb|?X1(}Q7;-mYr; z4nAvy{oCxP*pI7f+VS}5q>XFr(Sxa{(&OhoeZo@9QzglPF=Jp1=7*GSVWVg2ac$lz zS$4PaRSg~ocn4ViK8&i-*MR$q zV2EzLf>m(RSka7o0hLwMzaQ5t_Z@z@D9`r`A-jeKW%lC+=%ca&_(tJhPbwu$MS&T6 zn#K=cJhA0rA8iV^dD!V+DO1Y-Lq`_q5}%A|=JvF?UTm(fn(I9CWwIujlGa@%t*70V zXKrKjSPT_Q@v{{MtDEX6701FN0Wwqkv`}E22IHtDDy_c|pP5y!D=XrZ3H}~2uUje$ z7GND`7kC@}!e-laLdaoo;6l{Ok(M!p6nW3_$%mW7DE!-zz6$dP{^Voq!V|0}?N!X; zN8eF#3trshV{9E}7cQ=RTSGWh9jt8%R%;^nAa#8qjJ)@M!k^Q(E!YiR^!y==fI_2( zunkjZ$=%9wHNDo#a^xGjE#)q%Z(})pm%cQ8Kdp=~R%tN8_`)ajx0IFC($4aOY5K99 zWrPA9EYIiYW77}O;!fNN8>y|6XQ@UHF7Z-N7nXq6IManSh;)s5BIsSj{o+~4J)nz5 zwi`DBkKcwUR>B237R4C8KSr4-HezOq!iTXBhi0VKX!bC!!}~c}8p8y*EM~;;StzA@ zF$|*~wOHd3W`p$B5oYh*l*~tWnc;8qw4#@1IAF0idvQJNp|5*6@63mi6&uTs;x53` zS$aK=0cbXiIKHz8XQnBX#7i(?%qMYdpR3b)K{*ppt0x>@18NFgY Oog9pOFt*QP68-^fMnWwB diff --git a/_content/_hak/rune_prc8_top/nw_c2_defaultd.ncs b/_content/_hak/rune_prc8_top/nw_c2_defaultd.ncs index 13a97ac968024dc04f0e7bba53b54f9a460b88d4..2df68be60bd08e0805d09fb058008a66cad03649 100644 GIT binary patch delta 28 icmZ3_af@Bp&pB8j%uvt3iGhLnmK*~RaBR#t&I|x%=?1+3 delta 136 zcmcb`zMiAr&pB8j%uvt3iGhJdUycC?I20I|m|0jEI0J&5<6T^xLfyf_5?Tyk+D@B6 zn&JQd|9=!1m>EGd15ho)Ck7y4%)|&1`NF^gQ6UR5jt!=og$=|7D&{RHN{-LZD=khf KO4+Ejff)dP7bJfG diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurablnda.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurablnda.ncs index 69a359ebb91e66baca56c7123875be6c2fe70b8e..e4aa3c2ba74fe1d9c464b5d70a52fd0671b82374 100644 GIT binary patch delta 960 zcmZ8gYe-aS82-NZe8(AcrzFo&YaHh|&XlWZqqVziZ6VI=24Tr2Q+X*Y4I5)qp$p5h ztlPr=skPuqD$*c>E(CV@gtmnx-R(}{-l7W$fsjR#n$|9BFr9N|g6PNj&U>Es`QGQf zocH+8^zSd^S{bS|Ru>lq%NXn0dCuimd`1uFu~_UsCfv+?zb1tg#X=^T$YzZBK1QXI z%SW+O$@kB3B^h25WQI`8lkBViB(6?+EQf|G%10FF#cRb!fqgK9kGvfSi#_CZV6Ezr zqasP=LwJ=u>KuxP$-wtBbxA3Aos{_ivFSR-(gLI(GGn-tN8)+t+p$>7BF%*=EuY+9 zU_31yhqdXXt2nKlaOVDIWgD14wZ6ld8@4|3IR4OPBhTeROwV;pSlHaNCgw&s{W9sN z6H*!$WVBF08-lYnc`;+AS(Ut%86{`_D?7h51WRD*PJzjombFMCRlyt@|{gsW^#K~+hnHsPzX`l_m;*) z++m+&x_6Lm@&vQ2ObMrP*-2==&}SJQx^?9vUK{nU--t>>xt0P0#Z zu&8->nmk~~OethY{WcS!mQ!^8mVDl_K8t@PSNzoM;GOt&|6^yg)?Ty!G1%dgw>zqm F-T`{Xk}g|nlh5EN>M~alBQ%(Dm5cvMHEy-^%C9e z;U6+8g0w853)3#JlIX$SLM;R7@{m;4gP_utFsCzPJ?ypj{x<)={>|P$A8|()Ti2IY z2CC*2hgM3`<@~r+mo@(jqC_I`OH$mDF;B3}vYaH}B*iC5Qm_Vc}_f)(T){i2R6F*(L7vTsG`|r#s_y9o8BSdIm)4Yw0YXOT3Jb(*|rfwg)OPFCss(r+9%&{H*sUss6Hs%vR<9I-8J^cbaeduRy~i)UGb%^Tp=q8x8J837}~y`4kA=PPH|`rYsrOO4W*>udP8k`Yx2#cc9Bmr)H+R~a#>t#e9ttALpxh&8qSrw5RBZSRp^cA^c`O#ARkUd!_|!^bHs9k17R2|uaC-OW zY4Ls~E}OtiGL^pB8UVa9)1V@Q4t2d+t}nRjqD zdY;u4rT^jLoZGmaS;_h-^Q<@Zeo{n#AC(>-d)#>4Q^ejH%w%TaBhOQ;J8;%B=nDO0 zl@gglliuwLZLv1b6UIExq0sHde|pF{XJLh3nV1*RtWnm_nKG+Uk)7Z*J?gXU0@J1X zJtxktq6NFAOq5pWsPn$Z6~Z5O29D76<x-6erCzO(RDehIvwZ8Mg&b>W<7<}?Z`jB; z$CE%*Ej7d()Qed20R4*D=69(9tu0kN(5VJmqLOkk_2vlq@$s%09mL&TvvdjLZ@p|H zg{|u;4Le)6J5T#9vqQMtT2E1Ub_eq!GR1;cxJnjlTBoQ~vE36)M^W0=OBwi~?PsdR zf%Y&hVz3<)#)kL{bRLO#c*}2g3(qXKaI%ptH8O0?j$*Mk!xAz_hK=N|R5qV4m2o{j zOjp#09b+0DK;7Ok9yx&vhfd+UuD8?F3H!G6Vu9IxIjBwy@x`m^@5CEEdPmhB>2lI1 X`2P5mE55`2*GCxd3#zMqO(~B7Mw<7MOYVI zM2z4MSyDk+sS%|;22nv5br)sUMf7oDQF&2NMut>s?;4(I1xzH*1H z4betlY{}xtYDqd*JY)^Xx;9EQJ3BiiDK5#l|FF!moK}8FidT{(e;k|S02SbX9OB{# z{EDBJ%0);TDkDLQ-aAgY{XZ%B+P0TX3mGUux~OOXu=cA~4mF39rI%+6`N zwpFuw-{^Jae3@gYXHc!W*%rZ8HN*>&_-1pUN6lk(5w59yX8mJ&l41wq_5)^pSNgLs zh!J%G1}d$Xu?MmpX|3dip}7!sJYw~!fhk3$b1#Q##ARm*kuDw=CfQs%l$ptfx+*(1 zy7u!D2^4hT*(np=yJ|S=Ei`u*M@n$f-OCk6QSX`L#V16qcOG&0FxG1qxcn_XYgf6v z3KxsqxasTQvOeMSR}oiyKukZxkxODQ@XaI4tKa1DixvZ56H^=N6-r^%<_UhCM%pml zW5I^lc7CoIsvQqwrTl8mf0-3x&6Wle9Rs#sqI+1@)Jbd6-&D)JgJQfXs*n|B&BHVw zW6ce85EU&GGz5QQ9XYWlv78iKPHfBW_86OydSPm9q*}yV^+GMP04Wn`?roANDvPtN zV@xwRxI0C;NJX9SC9hLGo+N`biSJ2}7suPGXaM)xf;57$wxVEB#yt_1%JU71Vx0Vp zV*BSSdB4zQ>b jEBEbi(>rlK)sap8u%7;8Zo8owbgb&T@+si9|*ONfv}^l0-er(FE0`dh|fJBT2^cPl64UhVA6syGa-K%2BbP zhwmjxTK{ZD7dwc~tRFXVG^D@E{QCctvMkV=m~~8}{q#<_X;f=jpP$IDr}grav^U!e zC7KUU@g%>!W)K8QLj3BHPV8teS;TMN|D{3AUs@#71vKuyO#?_SYoHRGDO<<43$=l= zphT%i*xyTTJlkJHr;&JIk{*F7w&-MA#Y(c^RK+gyO}9~a7d}_)pdeNs^riBArB2?J)OVMPHo*<#k^z z+STZ53E6^&$cmA1{B23$Kd)^Dod9FKVvL(ITx|O@9DO C)BT|U delta 717 zcmYL@T}YE*6vyB5yze&MT2A-Q*}mPT)$(J8A|y(vhV$2sSB&QGz&QKqjht8vxl z<@iel;X+kJUnw%@bJU3rmcA4FMGvK%_Ly*8G_edn9*QpJEW{VFfO%iTCAlRp*SO-? z5k;GHoH=~xQ`xyurB;y|7VS4ubFJ=2f@uD&MweE`gfSTzvdrXL%Dy%6s~|Z9L6Fa) zS++5w9|vV0vxG1%dzrir)ACq?`(tb(BNhFo?gV#htXbTPj?c_oky>`m-$Ed zOV5IxXP3TP&1M2v^cuN+TZN< zJXBY$q9cgz8lfpPGA1wmS|ujH7A*2HbWY$3~_w2~5rI5Gb72#JKs z_G|Qv|7@QY#UYamkxf?2cidn%b=ciw#zg05?W8V#O_wbwC=JoBamF|Ez}_Ysec*S8 TIy7_z8_#@V;iKF#JfQvy#^v2& diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurafirec.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurafirec.ncs index 7e37f9be73203844dc034431884be1757600dd6b..725e1695005bfad4bbad162add4a5ec33dcf5c46 100644 GIT binary patch delta 917 zcmZ9Kc}Nst7{=#)XJ&V`wlrs4bzOIM*Q313&`t_*?G7tsgEG1da+S*HfQTTXg6N=l z6bmw_=!UHv>4KueiXyZk=)lMjGQm)>A}X+%*`nx=neUtD`|&>S^Ud3EjX&OCXIZdZ zTUd}krbG}Lt1dfL(XT~_1_uYf36fhd)}Ji0D4NP=K}r(@!9NbAVt{&4C1!J`6Lq4- znu0505oa{xlj!G+gNT>>?5#(Uw2!@Z%#uBNhh&qf6J0UAF`bfV)M1wf8lmU|u`eB* z^ag#yRK&Ryd`XCB^@vf(9n%waR5u}4NoB=}*-AFMrz75#fIW(V$vCQUJ)u0a8!$m(GdTwn~NB^;s0 z`iAN@QQVYd&HZAsgPH3el1P%m$`2;H7-OC)Y>?7<5{_+EaYRySi4PYf zH|ZaSJA5Cu1$LWO|Y222WDN_qeeb z(*9y9K}DjbV8(7E<*{kxi2XWGRW2h}Nh7-to0S|&4Z^0`aau8tWua5)jWIrk@3tfv zW$OMIqdr_@dhuLI#bT=!Q)+s2a#+h4H8lqclLtw8@gH}Ra#q2Us)$=!66c0^k`g4! z4k62No(dR3^%{m9Ysm5zwzL-VWdLbClz$vW&KX*E2A(=Qx~|A5bDMWDIa=LkX=yL+6{sT9qjSn1$4282`MO1S`jk`rUiT(}Mr(1c!Y;m4l*+sf1zV=+ zH47`4?1;y#zX%8YUK-KOj*5PNHoe-g&=s@(Q&Ic`TDN!eN64yf=9|z_T}(Ys#jENP znOotk>Ekwx)Rgms@YGK85bS}?l13Ih58AOeU~n0i0^6e7o#x}P9{dbc@?w; zTFBCTqQWe`I6@5MvD`$nl+BSCQS1vW7R#YF-sQt$uyII|+Eh1=u5=*Obf5a;(Y05@ z;oTE)een^u_&f&pz7(`J-z2eC`1b5b;~&KNwx(!)19KNY#WXzDO$7T5z1@~ diff --git a/_content/_hak/rune_prc8_top/nw_s1_auramenca.ncs b/_content/_hak/rune_prc8_top/nw_s1_auramenca.ncs index 864f53e035c58525816432941ecbf779d8564076..507f037451d927ea245bd5ca5b7b517adfc72321 100644 GIT binary patch delta 946 zcmZ9KZETZO6vyv5eV(eE=u#8F=v z=Qm>9=Th=tF>8i<26dvaqK%zq_~zQ>)ru6A?%;<19y{9P<$3ol{LD^7mXx!#(m0lz z`nymZLI|q^nx4lQ!>G`U=nrhv-8}7t?9hve?!vh+2U+vd@axh7*18hgf@R;bb-9tL z5Ls2t>|TqpnoJq0${@}hkVDlQnDcKUZ$L`^!WE-tQkmCYkiYqztf3N5t$Z zf0R&?fyB&4_ybOu^q8z{{vWBTg36P^Nd_Y;bv|8cW6i_yR30e`u{Q1QsKTYoUo1S0RQlkMAXOy!Q3jQd#S zCp+dVmg^IViY)li_)A)g1rxiX)DVy3O3Qls2xYAm{7c_shhJW9)l~{2Cm1$`HG%D) z+i*2lPqR_d?&Ga}a!b2kp$9mLQ}h?s?a?WK;XRXd1_Pao$d3D+UU~{eUG?T(d%W@^ zXzQvWKW=xq95a=axa*0QCp&4p3;7k9v-cMB`+M_Wmeu4k0i& x9cuV4za7H8@F;KkS&kfR%;Tl+AuF7OTJejlIKi@r6%Q{sqNdFp2;G delta 749 zcmXYuYe;iK2)yq>I)X6ct5b1@)mayC76(oAlv)I6uyL9-ebtZcTr)CQQvPFc+oG zn6*Tb9`$>|Yzmt^FrSLXawVK6-oTw=11c2*TVA48F|#Qhmz23|slk9^kuAkK^B6W> zgDvw28>d3aN)k^qgR~TcSJ_`V>5Ayf`4P;9bGW@?0qgDly@Zg@l5CVDNxv9oHIXA; zB2%44dTdp#9O)BvYAVq-#et$WPT7pq)xUU(Lrhu|N<6s? zX=~k_+ymd*bDSKHUF#0B^(cnY4I*K^i8$pU78O^strm?XDqo!mUr3x7Es13A?t*^9 zZ|-bI_{R6aU6w#7$pT7`La9Am#Fq_IaL0KKjgF4~g)zn|lubGczU(zhe&7wviBkIn z=8hF&=jK-83#UXv`GCxAH^oRrK5^?73`D15&-R~uI@j+pRk$6GHR5U2t8lKb7M}fU zWPXu{@wMOSF6#S@NIY0V@38w|Ccn}B2N@#bkgBD9C_Uavjo`XUvZAU^VaEp{>au0} zgy9q2^cn39Diz{S!w`A!rEwX>Aj6qXG91ozI%iTKSsQ%L0#cCNWFZsOrU|4G_NKSY zJKiC;`I!G_&zkR2E>5;s=@)LbfQ-m@&7q4p;Ifhz?XEa$+?dj)=~8NhUs3i~#2AT^ zH318)B!v(LU6<*JaJ#)4I)-hnUfwo{n)5LzYWv6)gUCAbfGezG+}V;uUe$~8J56jK d5t^P(t?p6aw9jYz!y4ed*T?g9qUHYHpnpK~5l{dC diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurastuna.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurastuna.ncs index 93234a429c4d2f86691b1d57dcb8424239619f7e..b6eda589fbb9409fb2111e0841adedbf4e1b5acb 100644 GIT binary patch delta 840 zcmZ9IX-HI26vy8=-Z$gIl%&rYmvNpmj-{z-WJwE7O*>&`sUa>{N;(qihomUNA}T6e z`nO3XSWvdv@&qZOl?){i`&8)%Daa(D2D2aodou<_AMU;9{?7k@&pqeH1OD}pP1)|v zYJPfZ+6qZ(+jrNLC|K=8rif+xLTnWd`hrrsf)=50f(ac$Wi=dqB7>b>AlWXQ%|=zn zadaB%7zR3oeK43EV3w_W&hl~fZqA6`4vWhwBJC?>Y@%&XNvqa-l%n~-D9Pu zz07Gn*bqCx^ZJp~YDS@Lf>WPjliJ3qaqy~FIAsr7EVzN!u8~@b~@qTSx?uqKcqj(6}A5wh3raYd=jFHObP$1{{Bj=XKAETBP<|r68TtcVnp}`AQzNz%U80xxQB+5Hs48B@ z?G;)_v0G3i#>%?LiuP^UbQoW^d8r%kx3AYpO8FuRMn!p{@uJn&{x$TJZzeaQD(taN zVep}Ux|{yAJ_2$JZA-;4Q!_Fu8z~eU-DV6{-lPpUS`|-|xK#y;$8ygCx{Z2IJoVzd zCo;+Aui;6Nl0$tD>nqexBLA(Vk1dGt#?wiC+L7x106K_*nttv&3-4|-vT8?ywX;4K og``M2vS4l9HNN*s8?P&gp`BW8V~vq6;MwVqTv?!9KXbtF8wE!3_y7O^ delta 695 zcmXwzSxA&|6ve-L=bIUI&`jr>ahc6ov~eB5P%=$YBrOaP9FrUaH(Ve!2wEPrM-wg1 zgBet$kY*ss-y%wah#u-8GojT7g|eU`rG!9p`evkud${LtF2B1q$9EobbX94Mqc%6& zcTke9Wz0l-WwtM%K(=wpG1SQ}io=Ra!DU%tj}McwgYEfvA@61PTaeZ%yM^8nj%^Q6 zp&evfIvR9V@ms6Yk`9|uKS?LP8Gc2u`7f4@``9?Ao+U?qmt>nHNyaNEQ7jzqLqJLA zq;?D_ZZ_`0U1cJ|`8m|^n9yiC9pP*XnKU;J#3rG@5DnSv(wjnA>S8!MWPZlRmny=G zf`o2PFT{G1Q9MqFBXWv`#CB%y4E9@wDHjVKGlGp;e6xHzw}8*Q_F@ z^kWP!Hi0zpV!OIr)(^=V^~ngVITf6!eM28qyb*`}ZT#AfS${FVFH_4VRQXFpWnGn) zE?~4_O!LSgAy}uNCQwfmm<|;2*p&DYD3vK1xlOaQ8>>xK`m@PWFc{oIPqPL7=6RYy zT1zFF(c6+wGNxMU^&?i5E{(&`T0=!>Y;`8s!w*SerJY?XQK?4UXkBL7Mqm37MPXOR zQ!2+|hnqI=rvqfeU}qlP#*b)5Nj*&>eq@!~C&lJS GzwRHaFx{sB diff --git a/_content/_hak/rune_prc8_top/nw_s1_aurauneaa.ncs b/_content/_hak/rune_prc8_top/nw_s1_aurauneaa.ncs index fc49bf0b91d2c8e436cd4f3b26fe10089630a04c..34bc16701b34d94e643876635510c6b1a8e71a61 100644 GIT binary patch delta 888 zcmZ8fX-HI26u#%YH_oU{S>rQv9OpSRT3Kq-CKP2jWzb^KR8SMg{qRRb zSf5cr^&_E8sO(Nj{b{9WAuED5AxkI1z`6|>bA%t%-s@P8-(a3q6>qJn| zSx?0ip2RsZBr877X+oh9y!#PWcC*(mUn_nab;FkU(b26c#v42wO+z&~H0h~>(_i4j zuqmt^GVZz)d*UM6X3TMAa;g(`t~_>^?sKK#v}-VHHe7a{aQLsAndE#yed-p6f2O$^ zMR?`PK+zy425Wx%7ZV$K&cNIVYd2YIHB?5UByBy9X^^qBT*IV%IlP`7dEc#+Tqq7| zsLa^J<7Xjzr3*XK9Qd3umF<^M=x*TPYP7jqc-$>4$^6EJ9df>B5OKjZM6!-@K{oob zj&uGtw54a_iZ{w*d(c~`$yvIWI5-+L+1okz4wbqG`t*cAmr!j&tR_qOeX^dQdyRC( zNmVt2RqZacEht(s+n~j47EKt-PE}QBT>ZxoYW%>O**M<9){_1*OVb5~5Yh>_$Oad7I delta 726 zcmYk2TS!z<6o$`QXGTY-5%tVPotw^#mgZ#!6)TZSLrBO3FJMZfGo)jX3eiIuR1ZE# zS@e^Ig@hB8QNgq|w1Rx-Ey&71N+XRf5J4JA(ap|`1U>Av_WJi;|F>AZ6Fhc@4b>I3 z=?%VoZ@DC0C=MG6+2wpnw6e1DUD8-3HUDCnWjUgJl{5}Xk}?|6%5F+VFXwQk7d||&2VftnoSsFhE!BS$RAZY{+?8~0fkqmfk$w%~M%nU! z4HG^C5=`z`b41IURy8YXO%K`lSfym6+}y=ErDD{aMdT8*mLTi0!_kox)D)V~l6HW7 z5|Z3z%%9TYOIj(%%%Q+~l)YI9TSHuM3=Otr&hHaD>}!bgdeGz;;@mm>bX?)wB7{?H zc;M{h^;d;Ay@IN%GTKgp7yH%Md)fOHPnxtbxZs*f>dA_)MHP5 zGe2{xstMEe+5BSLe=i$ET|>2&jw94GO!rXP+)djt-dx6)FNtr>6&f<2v}Kf%u-H;f zhp@SIiNf&ttH^>Le+g+Y>faT6&aQ4o3c(bpr7{Eqt`ui<0a7&5>Rl^Qg)Bw_^Gtux z*Va$*7%I2G+kS&8@v7ZJ%iti$fq`HVjo?wxLsM7`rg~DN?twfhH&LZ%)RP!hlz+Zb z9HFq|7CjMfI;Lc~-;|Dv8?Bhyf1AhjnC!M-u=7Lws6Ns~`aB6;M^X^#8ey?TRCn*T g(So>nvNM*h!G89=Zr_ko{U`lE*?>#r4K&951ycdp#Q*>R diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltacid.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltacid.ncs index fcf323eee70f8dfef04708ab45011fcbc682176f..a5952b3100168ae75def1a9ca280ba69bd7542d2 100644 GIT binary patch delta 1067 zcmZ8gZETZO6n@X?+rAa(GU$fN){nJayTT@&nU+<@pdAwl>sTO6kOf#`M23VZs9OiF z!#DcjMyY0X7HK zrw%q-QRvU+nS-eG&!bz2_+32f1djRNH|Eq?)%2X5Sh3($<)@^admO$;=fGq&;`RcM z(cY;k*J#S(;xE{|N_FjpH?WPpUh#GympCJVKuM{fCr>lyr6sFF2OIIt!Y5cch1sRe z>?uO5w42=rP_pQE&W(wp#WRRqdl3vCW#cZEa+QraE)mc){(~Q_;kC$Zc}rX60)2;G?vG(@df|-r&Oj->NS|8ileeDFB<+A zA;~F&wzFz}g*B0;kil*LL+bSs1yVs%7>q0l^QFA0T4hmKZe~7Thw`O=^Otv!cxFE8 zpB_-MlH^GaG*{$^@UlONKfj4ewYqi7vx!H}iN*>alRtHjl{Zy0D?Ol+uCI5Dnu*UOvA zWyPQsWyH5?U(L@~FuLU{dQD4t_{B|h7~ZW5dF;%TA|%dk4eO~LY1_M~3;o;c$p`0- z5^j4>)b5zjad!+G8eH7{65edE(Qk-1oTR&WwlPc_alG*k4WTo#j4Zew@zXTen${}s zSd!KEpt)%!g>bpaVa>g7CZ;AM^%E+;Az7sDyvp41HEzA$PU+ay{247rFzTXvcs>ep z;a;?eK7y~sRd)Wr!EK{ppj`RVF4*%s-V=HdH8tN;7}<50^98WD?kU(iu4 zX0~47Jy%fw?h!=xJU6ZNiso&4frS2p4}sfyxOJB3ZL7)V%>pyxG2Rjp@pzw}K1KD1 NKXTO>QQ8|d`~#*aRA2xA delta 949 zcmY*YZETZe6n*dQ+Yg4Ax~>-JXuI}p7qRSvC>_&n?FN$#*US}=O;ICa5Z#zR9Ld;_ zZvGgZnT`{MUu=ABL9^=es0=Y|_>nLsE5tt%KN!)tX%;{u$($qg?OROr$CKyYb8hZA zd2*gZzwy{KF9}yfTw4nB3d$u(jP))#NS_b)pCQi^M9F0GmL%IH^xyr3g%t*3B9nU$7>+7uO@>$t^6@5MzI|DfHdzjt( zG3Rs3?x&3|4R(wmnENI>R*K17kDd(p!+(QUe1KPrmWugR--VznWein@`5V2u)h4nYvoK%WfCsAP`NAZ+>+`Xx`eT|C850jAmY#eZQ%FvaJ0rE&b?GAo0_yJm=+`CwBiewIyg6({0sc!|L_`$=KE2 zU_7)U6@ufKXpc|{ocr8Mothgdo!oS-m#9J~p51qi`Ctx$(H_b`BKieY0v!t7#z+Uq zfyX-^qEm==Dy514e}lA%ln^b`<#@*sv Hyx#B+=o?F8 diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltcharm.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltcharm.ncs index fa6e617eb3c74cab4e8855b1415a1d6d03435430..2a37c2085104ca67b73609b6065c6b17a0e96a5e 100644 GIT binary patch delta 1134 zcmZ8ge{55A5Px@l{W0cZu`Qd7uCK4{T)TB`ClfZ@%2vlR#SYyhvN(an7_vnNF;3%H zgB$3S{qzL)$IuCh8{h``#Wf~Om?2S@7|@^rB@u&B&`?~Q3!xg`S`9cQ`PBUet!Or zBs(PjIc0B zp~&!8Dubnx2U8VGj!_XbpGKWVvF7>3G?q?~E|kxU3xwQw-#9%L8ZkMWHG-}ol8at3 z-q3W<5gH3Er=g;NNf{D`P27-8iK4SQOMQyKj}N|tUN5U%=>fEkQsSX7`?T1HnD+tw zvZ@aQ2^v_jjA}i4>RG)8ji1r<>RlMH()yA^SlB`{CI6uRRd%iP9>9Vonp^We=1oFMDtc6wIe+Oc zgti(iWNyx&ez0w><~=&87Z)V1UY2Ef?V10uDTk2A-K{88SU^^8{~uDXm!LG2K82xJ zIEokRwX)f-VJqUf_vm&-672M$qkz;%94}O}pCS%~ES9Va0iK^_h1CJXuZq~_nnRFg z7OeS{(UwM)`5%kRIOXS;ZFJ^{jhQ#(0iM1@Ct^N&?uj}4JTBsPJ^2kzp5oT!Y)72} zrc=8_lv@Pr+-$PG!2s9U@rKHKaio|tvoK@RIvH{)`r-^;8l;Mr*YMjoO}3Qd#moG( zk=1RF>YlkGbOc@;1G$M{8E9LAGKjH3(NhB z)AOFDA?5BKhiUfx?sK}d4x1t(&Qh)3MqhM&f^T~1M0Y0jy!HE%H@qTpx?iGDZx-!; v`!Mc#mR0O+T!Gu>X?SE1o4#bFqsR3yMSqU{X4;S^_U|^OA5&R!e3Ri{8;@iw delta 936 zcmXw1drVtp6u;;6w%lSsTG|!YK`%F*4J`%Am}6o$Ua}V>2+Thwi=r;ee2!#}h4FII zDQ+Xf-=Zv8_99^;Gj{A_#>6;$#bhyrcEJa_2gw$7I*dlhqBi2~-S#ElIp_Q3{C?-0 zeCO;ePyNbibybb(lc9p5N=drVG?4ZVokBe9M6h-du7^_wE+3=-o}mcc((U62@L{h_ zGnr^%VId*O9!WoL*`!-CuqjD4OA=Vz;;d};j1J5=;8N2$_ha}}zgL^Ks0K$hI7~?$ zo#U2tKbNh`cvj#wbvbK2cwg06Yer0WIUiw8J-|7BC|b5So@+6a1;Y>jZq1I~)o~$I z=oIID-zn@IMl|5WiK3-!->Qqat+ypPM_0C2;J2bBoG}e+C_r|cELwyOp>(#z@l@!v zqV^|~v~i>sx20qxWA6ldLW}WzUK*msn&KHZzz!XBW3Kp1wzu7@ov>M{PtD=t=hnC8W`nh=VRU)|T4550`3z5SGm=ic~&YT)bj(mZN=K{=S|Euj%O zBi>b$#(Ms7Z#@&nk|vUc=pm!r^Fb*;Lx0Np%`lM6`-LPcp)>LU#YFzTNfY%W(KpF& zwWEB{g`02x%x|`1V*e%H7ZrmCwk+Wn>T&1HAitC)21d@AX#$nepZJMx;Xn7f<$uE5 BI~4!` diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltchrdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltchrdr.ncs index 8f99f48770f5291e5c1b7d112344f81e79c131a2..1d69902d6e24fe8b42f1b972167dc162c86c7d41 100644 GIT binary patch delta 1040 zcmZ8gZA@EL7{2H2?FTck&bBnHgR78aJnNGGS^o{BZMY(Xg3%db>p9{c-Pm-sic` zdEe(f=lz@1^8+RN%d3={Ja2B9B)z+|H?faZopw#Xhn| zyqJo7A<0@vf`&gqNhzdsBWQDyv%l|Y3r^T9owBP)uZVOy zJB~_wCpq+d-tN+}?;$&HC8Tw#>SRSvwuKvAUl}O*1uVFW$y6r(wqf3_CsP_Wc^7qAWDI+f=)rTC@VseAZ%h*fx=vzOPP6{!Se!Y6WjV|Hygq|+QeAHqYa1%P zQ)JpA)U#0P+e2Adocl5vna}d;xx0y0p2GV49`fA+%QrzW8}MuXYSK@@ThKv1GsX&r z$QwfSsymbu#*I-cwm)=-JPoKV93b~J77O1Xw-aM|c6{(~h_a9Op0@CHMGi*Z%cw7I zCEqbTRg{X{YTb%?)yPJscE9j|qEl4I&&skWUsAmm6a;rDFz&Df9uuyQYgMu&!dY3C zADR4@7YE`_3V1)MA-m5k+w(uBRx7dM_||a_zAN_A?1}q*%Y4IX6Va6$s9Up0m$zeb zvkyl~#_3uYU%NJqC{o7*rFqP7wKz=@rzO%`XU8uGw0xq>Kr|EPAFmr^G+l#0sRe$k zmE+O(Wcp;E(6R8q<0Xta5R#X}VKZZ9({J?A-vU`=6if1}eudHGr~J=~LprvA(dwPN zwdR{7daG4bm&~87FPGU0q&HeHm#V{OyT7Eb`w?uc!QF-o`uw6mO6b}w4>q3BvTo>_ z2H7Bno0?cIYU#M#K>w`AYMD!lMZMI{oIl?aDmGDNEin(wh zTLNoKt^U>2*ieQMZmDABm}zn9GvhY{w_B2#%ykFmh_DMkJ=e{QU{6}n_57%26Un$4 zHld;Q&3MKM^p)oD{ X5Ea8e8n#TSr)&|S5ho9fY}Ne(>QYE) delta 851 zcmYL{YfO`86vy9l`qEZ2NTF1MrI)>V7b3JpEegs|w1^R8GbN=U? zqfa?9%Bg-|jdS%3&-8_o)Ej9{ZKGwV>$3D+Xe8C3=yWtOF)=R54oO>9S*M+pWdBHV ziX?$WEvIF(qpx)&6G3Yle|ZJ}&b8pA)xt^~m#j{96d`V%#YzaK97R^v8JtOMkKmh} z!)(t(z-ALVn?8l~n5?{F+d1)Gd1KUBy7kzkWa8KSbXZ)OoUug{uugg*$#yM^j0ITk z%3)I+-?{QB1HZUbPQQ()>x|JEP6Q}X>~uF6Oo_KT2!HNWELKus_9#Y2Gzs@6VXf!7 zE~DY|SLFruocpwGW~LY{NGCQAh>_{rm~+=)srM**dhpx#R=o3;u>KJW3q$OV;bLJY zyYsMi))>3IV&&{Fi1WX~#-a|+JJ43#&C?#^_u>|wR-LGq3R#?;zsSmH2&)#KJuS9UGJ0W8J;(srx)mG^wTQ5YjdxGAjZ0SQw)QNNtfN7WsvX2l~tC%^3t+85bFHaF8mq`X$QIS6$WOJ% zg)Bzy8%4D$3M?p@7b`Y#kB9ZPLt~Lh^EuevjvqA_ihVBj^kcJ6XFU>4zDL=!0iXKZ zswY>|C7eV@U`uJ`L=7>i@uD}yW~FfqWc?-5*J7UE)q4k=Qqh=z=;< zIg585k57*`-5fLBtR&OmiBR39C(27JU8-tqGZb9ix)5bstz|2v@qJd4s>tvMRaNsp z|CgnxGYNBLncl@_NV81;OaEh9tqLuj5uV}Tt%73yI#m`NCGHnEnTIM7@#I~;bQ_MR zu4%D`bBY_;r=2KU^N05La!KUv{qNpQA}0m@z^J`KtQx? zFSb$}uI%Wrbm$85Uv%N{&Qi+4&7I4+`Fk<{<)DQ-+mTW0b$8a7|DF@7Yr5NF_9S3- zLym^w!@@sNQ`<*(kg}(kHe%PF+jI^Ub%hiUQI|t=@M~SEc0As2>=cUXS5pA**1Hpu z9~h_Ep)z%3J1L-wEA>~I4}Ob}Uu~ms6gFI-A~+hobPta-g1orYxQKcXl*ka0_C}{C zngZK0m5gvHWIUO06N!FMM)p61@%-Ly8Wv&uMlAFKMqeM{{i8@a5`~LR*STpF-TQ~Q lDMHjXZ;0b<0^glz=c+nUbMmZ}2I2YeXD+!VLeDhX{sD*6Svmj! delta 948 zcmY+Cc}!eY7{%ZBy*Eo&VHQ~iX5KtzhDw2H69t4oTS&1bfpjD#Ev;#)XtZ>S+O(uY z$FSM7Qy_tpw8liz*oB}n3b%$@qb+}!lrGZ7e{7;oH!VgZqLS1~!8b!1;~zKo-rq^S zb8_#w`!jdl=Vdh&wQAk!{8iP{S&=14A~{aK zl@x;{0q!R=MI%SE*frdH%Lc!1IlDi{8ea};H__-**;R;c-!rVWB39r)%x6-x?ItxL zInQBfb{9KypsvUiquE9S8S!QAZ=CtDi01uimhESZ2&}Rp=1<0WfsHLM>q9ivvd*Ns z^d!=jg-9D;Nkp2I6sHc8Zo*x-k}Rk$^l@6HHt37c=y8x}I*_0GNd#+)+C>`gE z{ASmM1URUJm~3hO&+@^udpY}_2o`%uwqCKrU1Af1B|kFz#?f2qz=vfeZ2S(BWv%R; z!mc$Z*;|Dt*3Ph}7Gq&6($@XWS?ggd?`Q2a&X%8H_akhtILhwVdr#QKT!o9+I|SGI z7WOsbKxGYoV?dxaP28zWX1=@uGaDZArLQr)@t)~HJfoz+U1t?jRR-p-#*q=s5o)!G z%vVl3^&gs@h^v^YS}r!#EMmGV>NmYld~r<71@0*HgScJyJn{JmsvWu5x#chZ>{WfV zABVPH=5s-vriF_V56z&n9ppr6$I~>3 z>JCrY#Q!_L(EPX;r6n92@}%4({i^6IDM74{y`)8iPdlP?T{Lt~$nqY$in)3R0*6O9 zl8?1rDabf-fANw0#Iz-O5-xV9!W$Xn!!L>Jkry&}w*vpbI~=?!#s@wz&^3gHelpiS Qm#F$fyc^X-B)Zl3FE%$w9RL6T diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltcondr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltcondr.ncs index 5320de0b4fc41e1598d8619c8f8a02204f6b3d78..6560555f84c559a28c9f214011facae62ca35183 100644 GIT binary patch delta 1083 zcmZ8gZERCz6u$52?Z=!g8~s>HKWR6Wu4_9Pj&Aa?RYt;QiCLM^Z0cf`eT8(zgmxes z>NF~ISe{@OGZZ!=u$hUx1Oog)6p~Fs;}pSI!U#mOL{Nk!iZk=}b|%LA4YV>0rcASwj`3SLh-GdTy6(+0#XI&ux-fZB*5i?PssLgkdRopK(rjG~lh z5hQ!xCP_IBxNKQL?j}5Jb&)%WfMgtd5wZ4@Lyb0@jn7+EN_G<}*PR@De^#deNso~) zc3;tu=?yG;)VRl6MEYt;!;aT zA-RXpQ4&|*&iF;6nD#Eg$|5Zm`CJ<3kQ{tY3R)2HeMS06_8PId6k02`lmC6(4%$36x$o0*-#eMyikV#J zLW>WJqEv;DrQo-g@)y@j<2kOBX@caYMNwRP_Fsk|W`S%&C3q{zE?Zz$cm0p4R0^yj zduWz}TNPnSf0!wjZ>Tg7eR~Zrt$ILHFQakwO}hFXuUca#O1;3Ft4f*Xa)n)*`dkXE zOAlw8j-RR45M500^G^*knruLGm67+8}}O0DdLxWOVid|8rxXip^krQ!C%m@S~2#wo6yi^UW{0#$quRfNjev>L25cTS6kckNsPZvp=vY z9#%KYPk0W~aVsvwQ|u%@NNiv>+(?wOhhXl0UcE2#yoDp!)?Lp+xYX@Z7iCunce_nY zSME$H*z3scOVO? zm#CoiZ`_t8rU4_MZibFC67-i)XqbF5zb)wR7 zf?Yn;IGsZ8H0a5I8OlHGydox*S0=Vzz*ue(TRziz6}GIzCclHt{rJkCOBVd(_wuaY zaogW&@-(?LYsxJ|)mo!DnR|N?&Yp*LN*b&I#pJ$~g5RWIMc^)5YBlK`tjmjX_Ncg- zH=j782bCWci>w7c=Ilyr$Un(}cC56_!N2(>YN&6;zb*~&Ky6Z0sLN(EKj=bm z#sB3NwD*Fu`{oYSMXl5bB{pk=R&7e7i+F4u5A5zkU2D zoiBH5W->(Q=cO_=VXAtJA4*9A{f;mA54T3F_;yDQ_pg3OgF@aJ($hgib~n>mOzhr8 zUVOP{IX6y-);&R)p5b}zb$Wrs-VklU%e`aNf#I*qC=>p=LXxq)uG09uGx^UJ+^vhy za@gvXx$e|)ceeVYn47>vpxF6gdp&Z6vVcwojR`*__PwPE?umosP3Qo4V7c?qQ0t zeJ9CTlCs`2D>OC}J~tP$qY}5xc6O8?#bRgcLHI2D*{Vl{)hw=BbXvNOX~&%e%lpa#P80_22OtqGmFzORhY}ha)l%-;kG1O6lS9ZE_XJkp2cdni)L`GFdc2~WM0^d z!|rZFPE8EtID$2v*Y&@~;@nf{cW2_!yd=zc>;~HrHTk@f%!03Ikd4pHpJal=yMvc3 zfz4+UC%vh}rW4})<1aHW{up`1@3ZF)QoD`V<~3lvIKcXINLdIHH^uUc(E>=UDN0#sS zA5yE8XnFk5I0l2kFi&iXH7oK~WH65%Lg^C^$ckX;b-uh>P^;Gt&|OqsjV0i(QGl{bj=TZrBn4J z%8BTzeIa4AZywO`hzXWD2md(vP<@#=R2SA#6gOWzN=MPQv!06Z&(0OxdQk*l3(0g3 zk=pZgru)H3Q@C+~9XzIKOv@_iYz@ s_f=+dEn)hTPOcjgk*?3QGyw0X(}oQ{sq_0c*7w-)MNhI=*YmXQU+D8#>Hq)$ delta 879 zcmYk4e{54#6vy8=ef=S<+gRxa?b=t~9wRO7+nR2SZdPEM2pUQmN+LhRL{0pI0V+sf zHR-B`ZD2|FP39bl0T(tF5gm7bkU%n!@Q*+WV$|srR1jl`u#m+VG4r*=#GBlklh3*5 zd%idKOx@vu+iZ`wtoLmQ>$T02bZQ`L@1@_fN5<(5@gfbAR-&3rG&3{vP?8nNSZ>*5 zoK#>}CD|-VU=cQzyI2{`-kggqo?>2b28kvo(jF(P6ZpX6W2FISJoT)mVAtKSd#$p% z+3G8#yrew;)+My_FBwj!QHAq*z>8F)E?N_IfQGWMlO5Bs8zXa1;TnImrh`< zzL?6;p=&(n8jkA2HebeTSX0N5sM%vJ&Bxjm)cRfUYj#Z6s5WK10Fwn6t-Z))dkkqm z{NbIv=$1GfUP$b?gvPpMf+7LtMH>;SKg9Y7M(4UQQQyRtNt7>1aVUrWC4(FaAl5L= z!HB3^T132fA)2Efa^PM3935i+59nz;!2U$`h*Jcc6ynf(So3%X>(5{~7U#)`p;{q| zpKvf=e-l;9|E7mfR{Uc7wmR=pQnN2qit6Tni2g)iTR_BHtz`S^x>qv(BA3tYebUZ+ zd5-9MYLMuj2(JD~=8Hw5B;H1R;WWtY#{Fd_xap8lzq|@#Yjga0pV6TPV~H>L{9)sU zOAM^@5ue*C&bGZ{5yzkZzL-Bw8onH&|J9W;WpHEL6n`h>2{Jqz_uDt~mx@81cxk(z zCtClPSt@RAkDDot$gYp54`1(kjWn#<9pi)LV*lD2seN&J4!M0(G>Y>b zPg4bok_{xIHM!CHz9;|oK^#l2rx^Z9s`He>z)6qB?on)(sKq3rZ{1+V4!BYongxAR z1h#@79(_&^ego(crI?#${*=BsFSNb2pIhe-DbcaMr6ySG$wdmCay a{k+2^z8U(=Ojpo#;#Yn)Dt3O-Y55nh5g;J| diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdaze.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdaze.ncs index 5ae5e301d29c5944ae276d54ad4d19c75e4e521e..b8ec5d247066b3557b08467f9a3cdc1cbd095615 100644 GIT binary patch delta 1131 zcmZ8geN0tl7(dT*&;3B6Zrpnj6u5_bFDRe)LrG8wQzxs<)P}*Lq7>=e11#40<2lcM&+mPI&&T_G ze~Wv@IHK5Jsyyb+@DxbWKu<8@0CfkCoTC+Znwn`swd;_|gDHPHbUK=unYk;;c1isb zWJBmD>r_KpmSSV)05&NKD^~1O zGTB*yF2x}`vyF-hC(uEREBn}P6!i&CJ^hT%q}v?74+*|#(Y@@liKCiePO_ohlNfF( zm`^Ja&v{}LOT1Ch5`3I>q2!95S(w3t1JHX@={I=2E{^QQP`L#G?>nZ1%20mJb{KrG z7{@|>VmJD{OK{ldHYx2IP_6h|>a? zT636F&!a8peNJ^CKX)&u91I?@iJx-q#2Md0msiW~{kWGKhp)>GNuyftD@hZ*8Lk*+ zd(b7zYAKS|0hNM}Di!P}+f#+=p!)S1SyJ6AvMlF+`X6sLpjlSKHLSTb$1JG+A5*WF zX!Wf0EC&sF#XM3LDp*|4voc?J7kAg*p*U>Y;>I$6KYv#!uKMjv(PDa?m-ynO@D#e4 zzwn9eMaL-2JU>&6u6GIVBd28^N*6aaZ6Q8)2^CAzu%l#>KN(c-`z>if<9=S5l5OLMJ_0)pnFLzQW3Tt+f2WM(>dCvo4s>UzVBywu| zX$ljy#k3vyuT9V(e03YtqU-8b(IT9#+iE-%7aGS&MAes)AAx#DeBy#Nnp>v%v5;RD z$Lp^#w>r@LMhltI@#JEpHhw@GG1BOwJD6?+xe#nxNqzXN$wgn{*QS^hyCxDym(t9t zSm*(mH6&)Clsd~W#`0agbYA?h>w+$<(WX#nzb8?mw_$kCDSk5s!Ap+MOJ_ zfx^=z6^cgN+b6ksoAB?gh~*tK=seNJfzL%w*N1uRdmK**B D*0E$L delta 849 zcmX|? z#VAp1zlgt-V{FfY(U~efO*81pfW9Yx=hOjlD&x;sww}SD%Zl=h)eyNBPN>t~C&$f7 zvR(T?LNVrYQ`j_tC%i67K!I1~)nlmfejclIEMIVrAU>}##US_j#8j!5V#mhplIB7fwPj|J z{Y)bBja99k4`fb8eHZ1d3Moq#hrpkT9SHD2L*@NxRJ>-%1G`zr9KEk|y zt*F|m5MQnlO_gm1v1iwJE4i;(6uY@itdJho(|!U{h!eX=xS^r6>^i0k5-G4rV^QEaOA)c&7!C!K$U23c|e0`Y28LP zTyHHPxhq&5^I@uHjMRhLU(?35pKW1hX=&R$7)jem2xcfo#MS~1o|)NV{R>ba5rC@ R+lm(lZt{*6k$vGU!+&=AE8_qF diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdeath.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdeath.ncs index b66c290a610cc8f1e09be6afe4827efa2504429d..ef10b59a2f186d1ea7ba97fad87c91210374b369 100644 GIT binary patch delta 1105 zcmZ8ge@v8h7=NGV-uofQJi9~BJKB6MiEZxE=-5g=`zVG2=y?;FK^L?Jr`#hiL z{d}MOqx|D9oD?cqt5oC$a*8D>_Do08+Z2r*vysCjsgG<>4H;-^YHC!Hjgka|I7?R@ zth6ImW4EsI=(*n|IaLLEIx+0b;`GzF<5XC&!|htk>6K{oxe})#=}XOunoXm_h`J84 z+bsMG(~a~i%G1U;^`I#7{B1GJ+h@XXRt9ePOje2Y4u8fw*oxI+9>tbwR@@rD^=!~6 zuBx6%WA14PpO0?fiqFgUor2N-u_dieBba^!{r>IdKQuq%1S99EpZbE&Lg&T z7Y7gFAEi>W;{= zTr_y^gLp!>tfre-cWD|~-0(l7(I`%`G$dN~o-@pPHCfRs z-o$qclJVlc&$u;=s@LbEaQ|-!2f}*g#B2#azu`b>OE16qp%`zeT*&n+QQpr*3riT8T0TGmDM9h|@yy&lomyTSAyXpmk4 delta 910 zcmXw1ZA_C_6u#&5Z7CX9>4!xMZEcDM+VUYVFbxc-VMbkHA+W@_8K-Vh7b9e`gH0?8 zZp+k8c#N7X+Yd`5EX*kvGt?i#)GZ4k^(z`sqAtcQ3)#fTmS_|9_SOBk_niCO^PK10 z+~>}3+;g8Z!l7n&YiUtQy%49B-i$svj_xWOW*U~@tE#l#>krI2Aq44YVPWB+Q1n88 zv(AnKHOtDnYoF6ptyzyD1chP~+1)6>?P~-%-Jy zC^kAg?AeH@8l0o(cN}DAAx3laWMi&DPX@Fu`zt-~hZlY9cu-)I?|N<<*>)0}mfO(e z%V+a8wTUBrRwz!jv$+mm`*PVdg>yb1nenD?37-2hIqMo0`v;8fSgOc#5$FApG*c?~ zPNKx0gD<=p*c9*>T^BX*6BTrzKX94Nk(WwVplCO*c!^_3^NU2JT8|v^%$i2tvi`KG=^*07Rm25D8`uf zcH?5qIjq>a;}%ULYr7SVJKyK)E{yK1;i=WBY845|@CRW%eTr||PS8=*wzrWN!|l}^ z8*9Bd5`&sM9;gG)cwadU;>td6O+vHi2RACiSSO%cLQe|Aj<4ArL)9SLNMkLPs$S^(6S<7#{c8ab*ARd|(WF pKl_!xoR@1pe><0V9mPQWIPZQ(*7cv((Dq~+SV*=k{$LU#5?EtON5VFd zZsGz_=Ae8ePBRcUgBuc1z6&U7f)GtyGz3({WMnLw=n_Tej|7=GUtb{*-yiqx`?*i< zK6jTpIl-^}z)WbBL(TaU9|GOeXmkUo==u(W@hFeN%2Vf=T*#^pCapq zjL6rLVv;1t;&IL|V6``W(T+}MKKuHx!&$;bNAaOkWz~)g&Ppz7h8A=}E3_y@)fQC` zd1*IR7WS~mg7q$!u(-@7`X2edOYH6u|N8#Ok>|c(#+v~v89#6Ub)osl z4QV-QzX3d|1LLx-@kYr9v<+NbEI!r(B-@7^NQUj=?{FjYoo`@y`3}}DVPb(3)8&iU z@&T$Ux>&R0c*P)x5?EFF7nik*rn_$?4z^(R!o6HN3Z-g4m-=w9YCD(gPVaMwSha^Z zbQX!4O|0!iOSLQLGMc$)%r;KOxF=_+=4{79ilU2`u2Ybv%2oI4OBgX3L`iqY6h*oB z#0^e1U|80}8`*H_8Z)x~e@v4}qMB^kEC)Z=#CYs=z14Z5VUdmbs|#4W_zItW3ZoBe zKg(>dmGsjRGujTV&8C0a=X%qFrnjD}3h-8w&2(&KdoI{mbIC3 z*IMY9IwSm)4hf3*(i}Lo_O)U8iMvi$g_nxf9{;BfLEU@3wiE?*1|N zrG$}lR+)q|yKaZFb(mj#OB~+XT*%Mdhy1}lPUMN9!FNrTPu#kYZ_D+0Ik0BHhHb;| MbK`MQci?&Re>CeyM*si- delta 909 zcmXw2YfO`86#mZX*NZq=p_D2uZTYM*KraxgplHYFHjS4KMOkESiDsCL;jCj?21dGz zyLcgSk2){OE@E_E3wbqB_rn6q5@T5L5+`0_%*b?`F>DJlk|p@{L*L{*Ip;~<=Xr9@ zdG`%*?>(Lr3D>&oO8q6(lGN3^bJEvThaak}I5004i7I3Fl?L({6`hX8#>W1aWS68K zyR1_qY1j=()=Lsttm4cm?CQpPyTS!OAz`%QnBB_CAbRX>c9r9I`wUj%$a5&NveM|* zVCPw^c5G*7kr;MN)sq1^%A*PS35}m^_^!w*+*5xuvGp*X6wl`D2KBie!^K9w*SsQUNV8iS-_Af-LGQxLa&lF@abB=}i+IcJtjq=qhoQwpp@ukx|*eJYJPUeA1v8cL~ z__|HBhXc(0%SG3#2PxxH+4Ff}iOnP8^UlcpON}UBR7)tZ1aNNgzub3Ir5mNYh_>a4 z-q+p4J)L5x<}-sBc;i+kpGc}Eaz)>oux#qkT40)pd-*v~-n&jWvEGn_?-LU-xoIU| zabaUqhzDQMY>%TA;)S&lJ$-^VKRBq{hcA6gI z$Cd~!$K#fNs2hKNw18{~ww9BOrq+7nH~H%Hq)zm=*3w)Qv?-HanjcAvG@k1v3hTtG zw!6%`1gbZ8PzEkbRGM{RF|cFq)`kUS48so3d2*Y-7sPOlID^kSRg|08!TwxBvhE delta 56 zcmV-80LTCTMfpVyPD4{5RxvIxLI40F`H>Bh0XC7KJOM(n-Le4^0{{R30RsX5|NrO! O1O)&9lLE7+0$3ml7ZINT diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltdomn.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltdomn.ncs index 063a38996e3f5bb9fd1cfceb6129802f8e3dd396..3c354b308db1ae18be3aea532ecf5e4f0450e23e 100644 GIT binary patch delta 1105 zcmZ8geN0woSVnI=1`0LoUdYCP!J|}#Pbfq_V|f8_>PRYiBb@NUam!9HSNV^cfL~; zA)QVy3z9?NN0IdEC5c>s=J`>Obb>%4`$#OXp*$GBZl;ucFFF%6E4#61f^rlYWixpc zKNhu8%x9&TVvvfe4KjBraELxt2GC)kDW{D+@6_wyH)?xu9_<6Hx#XWL@%~;tEx64T zbFZQuJ~NtvJcd&?*$uKr=>-&3I*r>kI)4h1opW0?K}la3{6#l?E?hlIrpnSHP||N^jSqzp zOODg=M-HHOo)n9fnyNRT;VivV-Hl!|W!DU0#V#7F`4>HJvQry!0m~yazVR5AO;fn` z6qY$@xwaqOhvFwJ>{y)xu;LP(f3y?5@6(rcwsML#9od7kNv>6O-3^e1FFfTQq z4FybysSoK)@D|u;%3-Bc=mP%Izz)|t5DF;SWMMOp7Xn^fWH%bTh!-lEZSxR-ktLgc zl`!eyKUt{j6ApX21n~S-YPVXcwLX^ug*tLQyNF*;uEaG{W6Lytb&ey}FmG4}JlD;h z+0v(nIriF1!TWI7PYY+s4o)yig~ zaUBfOw%sS;B#9mEu#Vp7sKxy)Y`h~R!6Genj>8gtwx=0hq2)abFiG=oZRVv`cRdW* z)ZVq#aMY#_;51$8YJm`~jXCYbcP+-sdfXlcge0~tHj8+`Nssjmf{`xvd<##|2fZ%1 zMQ3{nT=Zn$dKjagK3Dw@pzhn>f@Brcy~{{79XPb45sy%A#r)b!pT*>9I>F>^cuDTx9K_n*dnZ7kj2T8OWv nscj^Ik?+~k$mcpk($0xouHyZ&(8REbE{~qUry?vqwpafjYlK?l delta 923 zcmXw1YfKbZ6rOW-mR+#eVV4Euu`@farK|!S!15{-x>9XbilLH}Kxz|BT4PhNCJ-v! zwFNX)*Ro$zZE8p=jg~f1mn+)X2Uz+t4Oy^h8~RB9)IP11A2f|6S%*P?+;hMC-JI_` z_niC3b&g)+)W%@5)?yUh_mCu=cMYaSX&dGmJosx<1`-X9!7CSN911rePIc64q7J$X z`*^{YrrR*6q^H^_kw{ESvR#tE;*>2bkJT71X*&Bpz*Q~4`Yh6X8mr|f^{r(+jGaDR z*4rGKMSI`JsP7Pa1ENk}X{RJ?&%d6WcSg2JlEj(#r`RJp?)}cmo|AaQsAAU+Gd%}& zhJ#)87%_5LnZ*~zYI5O8BLm4L%Qb^+doWS4hl@rq$8IS1 zII*R&nv;G+q%zEfaVS;8To}Mu)jY2$MMPv`vU;9ZufoL#hB(lV)|!)C@Dr5sTzG4r zt;!<78DHI@ZQMreayOm7;K`ID5W>Ji7c)DTac$3!>) zd-K0M{@NYUg2$)$<2THUY~k9f5r5P!%3Au8gx3DUa(<@=PgP_H_pYGqj9C3}E`zZ$ z9lJZerSGwCe>!fQTZX4Q+xUu#&pT^*cAfQxCALDe?QXQwOL(vAgza@rLfZ2l?C5SK zKPI|s_`r}zbO&Yn9l_pnbPfOYHqutCe{qgts0=+yndl3ZlZ+2Tt&XF4<|w6MX}XV&R{eUwszQmm`z{U*9A>jH`Wqnny}M$cv%=G8)0x{r=iG zE&k#glAI%%`@&LOG@=!j-S$Fhjba{Ub14?%zGEaM6RHPJ)3o?=Aa0`wF1`}y13joa zl8F-sf8+f`XA{Ooj`6N6@z}d(?KBlV`>V6*l4|NoH=)IJp~Rj@ Fx&`p?H-`WK diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltfire.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltfire.ncs index dd3f0df31dd86a9c73013374955da0013a6b2cd9..09ac47c7c0c26a6faf8e7caf1e2c84b696809a09 100644 GIT binary patch delta 1090 zcmZ8ge@stfN7WsvX2l~tC%^3t+85bFHaF8mq`X$QIS6$WOJ% zg)Bzy8%4D$3M?p@7b`Y#kB9ZPLt~Lh^EuevjvqA_ihVBj^kcJ6XFU>4zDL=!0iXKZ zswY>|C7eV@U`uJ`L=7>i@uD}yW~FfqWc?-5*J7UE)q4k=Qqh=z=;< zIg585k57*`-5fLBtR&OmiBR39C(27JU8-tqGZb9ix)5bstz|2v@qJd4s>tvMRaNsp z|CgnxGYNBLncl@_NV81;OaEh9tqLuj5uV}Tt%73yI#m`NCGHnEnTIM7@#I~;bQ_MR zu4%D`bBY_;r=2KU^N05La!KUv{qNpQA}0m@z^J`KtQx? zFSb$}uI%Wrbm$85Uv%N{&Qi+4&7I4+`Fk<{<)DQ-+mTW0b$8a7|DF@7Yr5NF_9S3- zLym^w!@@sNQ`<*(kg}(kHe%PF+jI^Ub%hiUQI|t=@M~SEc0As2>=cUXS5pA**1Hpu z9~h_Ep)z%3J1L-wEA>~I4}Ob}Uu~ms6gFI-A~+hobPta-g1orYxQKcXl*ka0_C}{C zngZK0m5gvHWIUO06N!FMM)p61@%-Ly8Wv&uMlAFKMqeM{{i8@a5`~LR*STpF-TQ~Q lDMHjXZ;0b<0^glz=c+nUbMmZ}2I2YeXD+!VLeDhX{sD*6Svmj! delta 948 zcmY+Cc}!eY7{%ZBy*Eo&VHQ~iX5KtzhDw2H69t4oTS&1bfpjD#Ev;#)XtZ>S+O(uY z$FSM7Qy_tpw8liz*oB}n3b%$@qb+}!lrGZ7e{7;oH!VgZqLS1~!8b!1;~zKo-rq^S zb8_#w`!jdl=Vdh&wQAk!{8iP{S&=14A~{aK zl@x;{0q!R=MI%SE*frdH%Lc!1IlDi{8ea};H__-**;R;c-!rVWB39r)%x6-x?ItxL zInQBfb{9KypsvUiquE9S8S!QAZ=CtDi01uimhESZ2&}Rp=1<0WfsHLM>q9ivvd*Ns z^d!=jg-9D;Nkp2I6sHc8Zo*x-k}Rk$^l@6HHt37c=y8x}I*_0GNd#+)+C>`gE z{ASmM1URUJm~3hO&+@^udpY}_2o`%uwqCKrU1Af1B|kFz#?f2qz=vfeZ2S(BWv%R; z!mc$Z*;|Dt*3Ph}7Gq&6($@XWS?ggd?`Q2a&X%8H_akhtILhwVdr#QKT!o9+I|SGI z7WOsbKxGYoV?dxaP28zWX1=@uGaDZArLQr)@t)~HJfoz+U1t?jRR-p-#*q=s5o)!G z%vVl3^&gs@h^v^YS}r!#EMmGV>NmYld~r<71@0*HgScJyJn{JmsvWu5x#chZ>{WfV zABVPH=5s-vriF_V56z&n9ppr6$I~>3 z>JCrY#Q!_L(EPX;r6n92@}%4({i^6IDM74{y`)8iPdlP?T{Lt~$nqY$in)3R0*6O9 zl8?1rDabf-fANw0#Iz-O5-xV9!W$Xn!!L>Jkry&}w*vpbI~=?!#s@wz&^3gHelpiS Qm#F$fyc^X-B)Zl3FE%$w9RL6T diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltintdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltintdr.ncs index 732cb9d422e3dd2c5113d9b74c4a8b13911a4156..748e670f86482c8063dddb0bc8f00c6843b5797e 100644 GIT binary patch delta 1071 zcmZ8ge{55A5Px^?wVlI_Vq0)@ZC|_AxUK7}Y?Ez->Dq~+SV*=k{$LU#5?EtON5VFd zZsGz_=Ae8ePBRcUgBuc1z6&U7f)GtyGz3({WMnLw=n_Tej|7=GUtb{*-yiqx`?*i< zK6jTpIl-^}z)WbBL(TaU9|GOeXmkUo==u(W@hFeN%2Vf=T*#^pCapq zjL6rLVv;1t;&IL|V6``W(T+}MKKuHx!&$;bNAaOkWz~)g&Ppz7h8A=}E3_y@)fQC` zd1*IR7WS~mg7q$!u(-@7`X2edOYH6u|N8#Ok>|c(#+v~v89#6Ub)osl z4QV-QzX3d|1LLx-@kYr9v<+NbEI!r(B-@7^NQUj=?{FjYoo`@y`3}}DVPb(3)8&iU z@&T$Ux>&R0c*P)x5?EFF7nik*rn_$?4z^(R!o6HN3Z-g4m-=w9YCD(gPVaMwSha^Z zbQX!4O|0!iOSLQLGMc$)%r;KOxF=_+=4{79ilU2`u2Ybv%2oI4OBgX3L`iqY6h*oB z#0^e1U|80}8`*H_8Z)x~e@v4}qMB^kEC)Z=#CYs=z14Z5VUdmbs|#4W_zItW3ZoBe zKg(>dmGsjRGujTV&8C0a=X%qFrnjD}3h-8w&2(&KdoI{mbIC3 z*IMY9IwSm)4hf3*(i}Lo_O)U8iMvi$g_nxf9{;BfLEU@3wiE?*1|N zrG$}lR+)q|yKaZFb(mj#OB~+XT*%Mdhy1}lPUMN9!FNrTPu#kYZ_D+0Ik0BHhHb;| MbK`MQci?&Re>CeyM*si- delta 909 zcmXw2YfO`86#mZX*NZq=p_D2uZTYM*KraxgplHYFHjS4KMOkESiDsCL;jCj?21dGz zyLcgSk2){OE@E_E3wbqB_rn6q5@T5L5+`0_%*b?`F>DJlk|p@{L*L{*Ip;~<=Xr9@ zdG`%*?>(Lr3D>&oO8q6(lGN3^bJEvThaak}I5004i7I3Fl?L({6`hX8#>W1aWS68K zyR1_qY1j=()=Lsttm4cm?CQpPyTS!OAz`%QnBB_CAbRX>c9r9I`wUj%$a5&NveM|* zVCPw^c5G*7kr;MN)sq1^%A*PS35}m^_^!w*+*5xuvGp*X6wl`D2KBie!^K9w*SsQUNV8iS-_Af-LGQxLa&lF@abB=}i+IcJtjq=qhoQwpp@ukx|*eJYJPUeA1v8cL~ z__|HBhXc(0%SG3#2PxxH+4Ff}iOnP8^UlcpON}UBR7)tZ1aNNgzub3Ir5mNYh_>a4 z-q+p4J)L5x<}-sBc;i+kpGc}Eaz)>oux#qkT40)pd-*v~-n&jWvEGn_?-LU-xoIU| zabaUqhzDQMY>%TA;)S&lJ$-^VKRBq{hcA6gI z$Cd~!$K#fNs2hKNw18{~ww9BOrq+7nH~H%Hq)zm=*3w)Qv?-HanjcAvG@k1v3hTtG zw!6%`1gbZ8PzEkbRGM{RF|cFq)`kUS48so3d2*Y-7s}=Oqj+-rU~kdYcM2BbPj)o05b1;oeA;%aqhX_`QH2P z`QAOJ()`lTY)`~1{nd-YMKMKrZ*Gr$Kh+>G;ClD{K3X`XnM^c2J$+SCeTsYnszqN^ zlMG~3zEM=OqQE2`=aHpNvbRfI(f+XU+@~;A5M@V9zUH3wi=uj^w!@1J zp6iS3~R`V9v|LB8o2hC^@K0*yK$uMf$>7VEEiKL04z-#!m(G&`JO&F1ejz#0R* zFMOIERZ<}zYa%;1KOc05Q+yT4A$A@S>7uR73tmIT;uknFiL}d&ON*DX^+S{t@8yUS zL&XCePU8NOzqqhfRNgt8IMjf;(pR})1hnKZ7ibt$ zOPL3$Dob$O@UW2#TPL#o7QbQk5ml9^R}7oS@nC&v?cLJq6J~>^$aG9q)fL14F%&gZ zFe1zHPBv1SVa9g+k7+h56wQccIGBhgc zDH?j#Iz;VqGjr+zQNMB#*}jc>6?sWgdIh!1=ZN8$jrq$~F|w+kc%)ivU!7L@^rPb5 zb(KUu%yx${TJbx7`j7l@1b^T21+fI@iLr#Ac=&U1ZGESOCWN~>b|b&F3r%*1Xx_Y9 zwcciQWZNHgArY|Rhpj38u2(PPRKphjdI$>|%lX{c3=$U~Hzv%~4SQ2R_2YO`6BR*g zUdCOAMO|}Tr9W|K=Lx!mjV%eP!(hur8p6x1YsrHPt)+AWyloq;uXyyP2hq`1NpXy} zY2NwQ4Z+MPWSp0N##Lc|{3m8SjLF^Il#M-mKBEe(Y!A{E)VG6zxZGY$?;z3BXG>`j^INo04XcwZ9f v&TIUfT%JC|&%Pnver8iHZ@UXw1HGKIi$ep)&GZSX2QP5dN22`b6PAAg+Dtx- delta 965 zcmYk5ZERCj7{~AP^me_Y&U9A;z`bvbN=U? z=l?q=&*{I(sVUBh*2R6>DnjM!BZ!{BLgGk|5Y`aXl`!qt|WUUt+`~A7Rk_l zl4P?afkijpDPZqN?+u4-lWHtYOR`7XSM+(`N+%}E6b!p^Se?KZE+2cVaKp8N)dce0 zs;us`_zcvW+b^SOd97GdQOe5F9mcEJF}~4>=14n-hLCGt zh;oM+|3+#!>u1zdCODYEXyqUWOR;Ok39mzChTs&~2c5G>WFc-eIfS0CnO z^}Pd55qsE696XBFmE9cLjBje9e7!@@ES5jwVE*AVY+OCd-><>xXc%p^7nF(Tb$Cfd zY@Z!1dvZm5&ELdVY7pDt6picL#6RYX{dHmH^j2~1u@l6XBO*i&wRzvowkK%!MMg#Yg<*pqQ#Zm?hFrU3-wvj%gTnF}i!boSif-uULIpTU3Z8 zFJ$N{#@iiuXV?no-kr*K-uVrfi#UWA;GSBvcCsF_|xL+c6Z$3Lz6$dBf> zRebc0INMey(=={(T%h0Z{>xFzR+psLBFa16SlF4N5!~v0obpl8RYfxPbnUPlU#vgC zAinO3(<*qn)q;6<$zUKON@j`bOro)SlKCxx+5<^ifNu|6r1j|Q@zV@G=>d7LIZ;VN zNF@BLGo*ik+mbwQKOc1?T4dNg6O;lrZ7OTsW+?XiMp8CmI`JW05yxN2m`q8h53za| z4j%f5kBotiyi#5X z4-GP$wB?6V14^N(uU=)`D$gTMWH3E75d*+&T9 zwZr(`D9+9gty4EFNzVuhLSL~qyA5K`9K8jp`vIn=rp5%xCh-3xijpkwPZIe7qVBsX zNG3rbks!y$2Jof})fR2YLhpI!U~)U1@}{DzpT761s9NZu_Zf6;qJn^p3Ve#>%2ZVD zN`kkk(svM@3bp#}%;%R)aEH>qbe#g5=;}d_QS{g>a892Hco%lI{fFk4Y-kJ+8yl_Uu08u5Noipm1g) zrDiUUQ4bqHF$Zpo=Jk(LZpqq(DJg6)%LAgN$4XkZh0TBN7exOYy=}7PW}`AdMY+}J zTS~WbyU=@x-dg@AdfM6E=Vt-Bo2fq7fv!<|c-6u*h0bhEY(u#XttBt;J)AWebb*^cNRqVt z@?&PyVVua_d(klA5i_{se@v4}fUIcpD2HaP%*U_%yy1Cl*Gew7EBr}PqHpH`mGrt<#|A&( zzzHg;N~MXH|HAVJc)-8$Tq{Qs*;KI#*uS17y;3j32+Ij?Ra)kQ4=H37K0^P z+TcAE*yBRl5LyFFuWuO-1A^e@PfOT*WloO2AbjY`gb%1qCL%W!aZ zHJ&%}*_FgWqlB_iYUCnm+|4tG@r!ZXsdahHD)oLGs@UOhrTyR_sw?K8Y+*KYwk zO&|}kg&%VE4wJqJ+bUbQ@JD&GvVhoqO=c{bWDX6We2E()B~JLO9%TDBc&us{7mQ(~ z>IfIq;-#e%9Be>Gp9iR(;J|Xkm%YJy4~iql*#8ek7Uko1q?!FK>CVdS5BZ1-PT{rX zZ5-N+v6?7fcl8JGpjkJ@E%|47tNduKR=Ef7+z`i!W})D*GJy zL!(A~p_)!AhA-{@8_xLPUVOg5?ZGs|-e7oa&=59Y?FLqG|X2z~k4Zey6X#$fmkQZIM z=hHj5x;wZeVeP$H#`NA#+q4kPw&b3iC+>5ZPt`mw3vu5O5;F)Tou_F?e%+Z+s0ksnwK}`^A;67#}Dz=Pi57KGd3E;_^F?q^}|*(CgJMU J&-Sjj{|gD(LQMbw diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltlvldr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltlvldr.ncs index 3dc34d7aa9710eca72ce881d7b0b774c334fd537..e8628c58ed109428ef14c5101efe4393b2fdf3b2 100644 GIT binary patch delta 1078 zcmZ8gZA@Eb6u#&5mewIypq-4;_O^6RpioAc2n)CsHse5+g^9ytTZ!K^SippjplojV z2}9YFW$eSbbY>2gpyMT*OH4!!CgS4a)QAxyi=SKE95QuIvEF;TC5!Km^Pc;h=f3AT z=j0qa%P)M-Ni`LJS ztnAl;dL3AkKf!71)r@;MI~y&5H1V!Kl?>J+HiQbS2o>gt)TKGhx4(w+z{~8vh;_+! zoDVE#(@88T+RA<_hKdexegrkke&@UnvFeVSiG7WzEk3}xlb9<$%())yyR(;bb`2b~ ziLw$GasC->D(zzbE2uAVf=|5RUkurQlk)$e<&`k?Ma|> zBB?HT3m@$g>mII`={6jFe3ri&gu81Yn!_t_xzWp?o?$b7UTK^s6rO8&TpCV7-pX8CkrA^e?Zj;bbIM6Z0gD?@Y(svqSvi>*Bz38#B3S73QOZ97z(p54~kH dz2{Vgd>K&tWy5{ZG_(!9&FeoF1;fuI{0B4ZPtX7W delta 941 zcmXw2e{54#6n^LQ^#?A@vaWWcUE94C7TUg*+2L?=u&w+sAf?M-W)l_7h6&7x?2paO zKp`L~Yy>|7{$U_)gfKC5&BerkaWgX}W|^C4kWER&ki|a?*-V<~6zl67-sGO#`<-*Y z@8*8zp19A+d%QFnY4kRgm#wasq~YY5rSH>56t@L|@l70c`dOLAu+z()O5Ad;VI__wE=5*$ znY}vf{tOMSQ|$JOl&i!@CK!|lIYkMbn;l*|%6&HRc*&0zwqM4Z#dbUltl+#hjl_vm zz|47dcsA%_>ooQR1C)n<15e;eFqfBohxsZS#)D@q-hQ`sQDp?Lsa=YL_4iJxrwVWN>91iiu=1*n*Iwn>=*GRD1Lb$T+ z4<4J*=oTo2Gsfj2x!p^aF9&r;|3qEAPdtpZn8e8QKjrc#qj<$+6Nx>WWJ|y9jwJ_E zMfr&Bv!HO_w={#@rhJ@E|0us!{M{Zk(h+PqFkm?6 zl`#Ce9dc(2`S5aQ4fkclh0cgfzv0)e>-0O$cSmU_X1nJpi92s?AqPr&DoMr*JuT+* zMVg<|AU^MDq#9Uzm4ZcOX|X%?C>bS+7(}c$#e7zv{9r#lhS7s#vTqv}#*fn^qmM{VyafAJq=dk0_2fY6!@!-<1ktT5Q<5^3S QqJOdlj9gVj;%c+$KUIS~)c^nh diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltparal.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltparal.ncs index 315054f49458135a5907431c67bc9d7f2d27b5c2..95cce179874d7b9b0bc57d0d79d4fe5507cc901c 100644 GIT binary patch delta 1042 zcmZ8ge@xV682>)ceeZ`hkUS0o?zk^^hYOCog9Lw^$QW2gV}zqov@~5ciXYQr72cH` zR71?6@R^HD6NO000qrH3Yb$B~5w?ZdmO9+5WwrZ5*$i5t>w52dZnfS&zVEZo^Z9<> z=lMMEd*ud?|Her*m33Nuk-xA)QN(y(Qa_FL4Mr(R?4xHWf5BxiP%IX^tEf&zt{l}A z-&BMAke<1ys76JBK`b{cX0X-^a=6$vfWcN9f({$I&tQu~V=WbJjuLin#i+xjy4RaE z3Fpuu+;;3|r&&a@-A4Ku!|q$lviIxqoQ^<&O-y($TR814^!S2ot&o3bA>uQ$H5*Z% zo$o&bv)@beIOa=3l|PwRj-kQ-u_dQDTUO{k0!!d^^UZjiJBm;JnHUTNEZPwrXple_ zZU@e@^$97Tho1^}abBLNE6OEKJt;yDy~OKn9)8$lObtjui?>>5;X3})aPpYacU(wB$6wr$rCK_$eZkD!B8g;TF z!)vOlmY@5Vmq;X>l3x=L$!uKr%zs)&|anJ>MMzsv5B z6WbpTAg_FqFIS1{1M*L-$cyG(wCW@l^ z$wra&)Lb%;u9bzQiR8vgl~!T8#fA>239ZdP@YMlOYdt<_@$h$3GE>G^t_f-#Hc~G} zUO7Q0P}TM_72@-@jr?wsSZb3B3n*)!q(AX{dyRQVmh2#Q7khTw@%-*tnnH2tF>;_I zw3e1*D)hAZs3Sg)Q+Oa;N0n#^yUe+XSAhfJG*ZP#_&RgH2i<#mDFw$H(&6hkZK!4y zogpjcJ66yg#5zD8AW}@@_&VaD8QhFyWFMFsKj-{*Ge zIS&kTY)N6cuVSH|zD$ySu562Kr_*qBy4tSvlSQ=9I?_f|lZhrLC*Ml4S29+TY%)%Y zvVKX9ktDFNo5tMi?P`0HfFvc3XZ;GYy0BDnu{ww~ijTe7*rjB$8pLHqmDP0?Ule<~ z@kZIlo)p-;iss=6gFav}A(b;_=d_)`7W^P$d;zTb2GCZ^$zKe_IeN(}Zog zg{=MeJd)9;a6~5bqUp?c9HRK64x-oM<%%JhZ`wrf$}-{`-D1`%AMy1AhJ6~gmfq%T zN6;rHiptdn{;f*XmF+Q$uRnhn$L9hVayv!E#$uUT@zdr}ejr5%s%i@HtXku{Uf62# zd2o4DS5?y%LyZ{Sc7hJzhwa~x2J?pKW9Q`iMeo+Js#emHn^+$-vCU#cXq2$yO zzv9Eok~M1#7NG%A4`9w(y15cJ*Xl4 zyK{{H8RwOSdApQ78&cEUlGOil%gS?<`cYdhC8ZP1&(BXua*U+@=U68jB=wVZ>O#6e z?t4krOA>U5_nYxB$t0}YXlJFdWh6W_-=+38iNOuOC z-CBh?Np@mQdX!?0Gb+}A!ctYLDl-4M^D;ac(J0JZZ&EI5K$!}}pe^$zn|Cjk4@c9cBb=BhD9cG4-i5Zub411F zYs_m`EnNq4vNyBwD(bT9IbkCnW_Pfw2^~3qv9ndYof|^zIE}6?@3Q@O>~wds-Hkup zXV^B-(q<9o@~p(J2M}9*oY05fyjZ;AF}OywE3YEWg^6Iru+VasEC)K*8dfRrS1M?# zG~nLBM?5=JkNWl621yNnl4Uvf`hVPogtoGpeu1@+7MboN|6}U)5@juvU*I5ZTOoh5 zHqf-dwg~2-Ds=Cd;V%s20fo~Jet-@YYA3euPiBCM#Lfq2wBn0e2X`FB-C8S6A>>#;P2Sz<&Qm`VepV9W;b%{-`9YR^(8MlpLxS3p|KW4T)M_ zO7(RGW7^+Kx5fLfex+M+z@jV+I`&0daQ?){d~z6J%@Oc4{1((O95{i3QzR9N#KDu7 txM8(0pW3^I4+$K7uayt`L`Y|!o^If?55`Qzd$b9h!Em=t-03be{0jrYX-ohB delta 835 zcmXw%dq|T}7{7qc(PFD*oN@!VG6hvWV6xkn9*o`ileH-=1;XVA`bDnb^p11!!2i|d%cSfFV zq1!bsLz0BECaRW3wbV}|WgOAKz`$2Yj+4~d#~N89NfK#7v`dn;k_1NY=s4lvs*!AM zsp*bI&@qG)uAy9S!bgXR?a!cf+SuwrqH`kK%dpaEm+i~-wlKC_LBM%{Eu+zz7%y@Y zbz0J4@z{?Xe^z8AeTiUG0IS@l+9U(=Q(`%0t$ICf&<9DjsQ-wWj-x3fIQki`rHr8% z^rR?kc!Vg=sR&zD=zzTq*F8J+(V@L^A4lB7F+Vjq!rB%FzA$J^ZRePs{~xKHw3QRz z3jJgUv9UwAru<@d216DxD}4&gp!V^ZWaz^{o*?h(7QMp zt+OxE4^fnDVQvqK>Kr#kbTnGj0Qx#%m}eASbIeR{#Mikeh;Q6Pb&d(HY_qtXdr793 zs)9*WFH9qDtwgRo97Bw;=wI}iudY*DKlw^Qhzi|O8}Vhg7`1GdPQZH;VOYOJ zrb28i_)OjCZLz>txPk`oyfB?#EB`>MDBA4Rk{@l`PS7E&-oAwtJlUSc`zwWUN1uip zUcgYSaKmMIimmh>i;6GM2Sk>5X#tj%^im7v__E22I-iGRJoGKqAC6b!kxs&0nn!8a zU1}#QnoEb0Mzoi{Vy;ao8$FEQ)A}fX|!@^2y zlBBWGs-mo}=rA#c$5v9ubr4~0MKe7Ve=0gPs@Dg5I=Rw??gM6A+S|kXTsXcj$b0j} phRPKqc$17^ppFafiTqRNwEFv@5T1@#t(%bC)WLa8qOYl3_ZQ~7E8PG9 diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltslow.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltslow.ncs index ab2bf8f8592dd4badadece8e6ffc6d4313c892cc..d59eaa30908585ac0f989a276c00b2d46283f2d5 100644 GIT binary patch delta 1054 zcmZ8gZERCj7{2H2?Z-5$o9#AOzwT~V2VK`P9WuIXuvQ@vEhd8{5|Jp;Nh=OU!d7q_ z6PQ6}Wqm@NV~E*|Zc_n0gpVI8NKl!?NsR>J%w_-yabQ#k#*fU?+i8gR$GLBw=Q;Pe z?{nTW`2(H2MA?yWol;-!pA(X#55wKrN!E$n)iXXgdYO68syZE;nwt7cl3kK`iey9j zCF{h(H0Enb)=Ls}`~;b_#!s z6Rkcw+$z?_F?<(dZK>U*qK;)f$=U+n`etlQWuD@bOSS(U}&0>w$hoBlSX+ z#}6(#Bh#r3ylqJxV=iWcG3Q&?sYyO2D z!51wLF$XO1dF&1>iLWs3b)@4M!h7*L7EW$bou1pS%JhZF9Ek`zEc4)|aUwhmTWf;l zV7T=pdl=8R6|+BqHn3upv{$eLc)Y!sokmCdOs`A(ezZ*T^z8nbz7;GmwJ Qz}BH3Y1u9w8}2at0~oMI#sB~S delta 927 zcmYLHZA@Eb7=7R8-hQ|Rgnk6J()MyKnJsOB&f0>j0mxP>hvZkcG@7Gg$l_1?QIzRCOK$vMe6 z&-3J*T%@H7l+#$>tUOr}F5k-7@ui`h4|p=2`j<;ns)Y2NjvR?+v)MCDHZcYgKhazv zdDBDZ9T<1zQt=O%a0N)s;18EV-YVo2Z6q~`)}RZmMHX3o#-bRM=R5Qj^^qrll-n(; z+-4IuqgMTk+~XpA*LADpJZ#445(ipK^06)GAlplth^pA`wwbmCvYg#ZpqVo-!$67>#-?ZX7x@OK$iwANVfL> zEj!9$v|&;FSysX&`;-$S9@9{?M3jI=twk;Q}nce!VOvOlpUF@kd5zW@1Yj;4TwpzIL*8&gA zW>;r)7JKS)#caKmXu3trKX{1KRHf*C_<~G7Hi_pSZRUIxTU=pWeC!&XoY%z5VBS8( z7c_9aSlX^|Ix#A8AAj2{R-XDTmyS(pyV|i=v5ITEYGpovidU}EJTuUr$JgR!v>NfK zkLJ7@7-)3C=MkqXF+6A3$Fah1y$j_m||Kxj=M4Alf~^*C4s00Jg+2{tzY-KE8~_1h@ws$p|0Amq}mk4A*o0H)>8gMG;ZgL^r^ELHNBK2MYy7pgM z(>rOXtqn0Ky@lv`XN=xo7Ul0gTSTu8WAL+&=(XpB|MTM}K8^V!zgt@_8986U%28EZ IIl9CAA6b7u82|tP diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltstrdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltstrdr.ncs index 4406f76827bb715f5f0cf2d843e2163f6b2d71e6..7819b3e1f81f9908f57693844a83cc6ecd9391a8 100644 GIT binary patch delta 1055 zcmZ8geQZ;86n*#h^+Q<4U@Z!5*Y>qjyM7HBjFrG#3p3y-Gb}TknFa_SZh>x)u+5Dz z6-ksaffM3{sj$gpGlKKCfJ76A2BXttI%k4$M2JR%i6DMN1k8Nzbtc63$9?zRbKbqb zbKbkdm-(e1*qThN@l?)>Gv~0U6W-%C>~v(Z(uMjWJ9->;_MOIAhlf2jeCt@iz8Zu> z4uqX%*;i!tsF0Tqq1xHOZZo#JT*Bxw8R>guC|5bBL)=z=xA5#|aXlF0?6~@>!r3`^ zB;@3or;!Q;=^BoN{5}bZFsk!mm?|EEclbdO`>99263ns>k41x;0Sn89|si-EZWO?hg4(jqO8bG92vuw zSPMt@V{?%UUsRaWk*Z+@cG)fdp4^4CMb1cadhc&IDe6fxBtBcsUWOKfMRy6_L?#AL7;9aUznJxB*X`GNh5V4v!RE2Fxb#Q z`S5L9!X3xOx@`%W{y^W3A-aLb8k4jZ1C7_|47!_Ek_$gI71A`go2$*QxHQH6Xm4Ia z35+)@Ik|W2!PGXStyc|A$RhKp@0hU}*IGL%3vD|;qB6wW{B#SO+CYBXYAc{q$lv8( z_9o z-kUE+=gi{r1Wmn!x9EN?*Z@W=jUG+Z|$jZ@|NYu=_0)p delta 948 zcmYjQe@v8R9Dl#xdyjh)WaquS9&qQ~@p6oL+>ekWRQ!QZI?B!~((*=1vz#MjE+_&F zk2bTV((qI2Uk=4lMj`C6&v(!B`8@l6 zKF|00J}2&Q>NYQq)iwCGm4_d#m88*B|Kelx+}OZ} z6VI;l5{HhWZFM(?pTcx?jBj=rS)C%fMrHo-OVqEMmI~Ya{;2Dc-7!Fi&q4BTt+mo~kf74UJBx=-KqS%-_UC<>m&0SB>C{r~l+j*Nk*? zbPEI4LXq0yBV}^Xtma>c*ZM`yGl?v5`T6U){Ami4wW=83y-`;B%*!j;c&W&R(pS@T z4NKHKoI0P4r7exh58eg-SX9INEyXBg#A7aCW9?40k)9BqvsMRgsM5u7vHx zGUGX=K}>Zu&{}xA_51~S$uvP`l&lifS;WrnTg)E_)b#YxBBXmR(MI5apJs9K0LX*Q z$tayeZ_>XmO_@LNK#~{a7h*<;3QW0|LsHOgY-MBGO+Yh5E4P zV49Dlu;Gvs@pta>;S^RK`i|e574o|;y1Df@cAY)R`*w(b&W>7X8Y7?ls%%ru{+`3- LVO@+5Z_oM{WuZB6 diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltstun.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltstun.ncs index b37d887d586ff894f1927ebd7a4afd54c890a3b3..7a8f0d384d7aa0c6b59e87b8876556d1e7f06fdf 100644 GIT binary patch delta 1082 zcmZ8gZA_C_6n^jN+YdAty0-FJcw6v;meQ8d0cz3F1^h}CAt@z16OFQtuX+c`4 z8S@?^nX1H1*(vuVIajn|%0-bkhm0fG=yi(Ljin|W_ZsP$A@q3%47PmwP6@^Nipo4j#ka72?ICi%i|;F~4y!8flq&B&rV0{UtmqWghe25uQ%Fe& zZZ?+ft`RO=(W)>>gm+|FUU%(3gd`^mNm)g?nbeRiAgh}Ghtz5%wkk=Q#NeB?LAqU^ zn3p@(nTevO@qE=|W`#d+*Dz6&&`+{sc7Fli{K7nA8JIfY;QOof%<#j*TuHp3s3_Wa zUyXt2yApnJLqB6_0%YO4H{FnF+{Im6w=w!+l80&)MxT$0i`}@jEka{g5vtANpVo== z)x-Sf`mm10(A}_y@7nRp5*qM{rc6B8SS{<9t5X?JTWP`N*M6kQ-b4*&4(_3wqmY{_ z>Gqw4On`sT6x6aV#NN1|xu8hc^1226%?-?hf13StG{XHY0hv8S=+G7R2+y?!^{-@$ zIpR++-kO7f)+ifAOWQ`Fz?W@hY!Uu$YtVmaO~~*ucC^>A0EXM`xyzpVFbm%%X@w9t zAoE!JZ$#a8L_%FcjqOLiVw+*@C}59K)q#`(SJL4}c-d;kTt@~Q#^P`RyADq{(~+l= zHM*ptG(nnp4{2&9^Jz+9a0=l-c!))KPG?lZj^N(0C>i j$4>0bp}lcT5A;&ww>&g>SGJ(af051Yh6uOTI5R=j5Dw zzMK2U5+D18^IIcr%A>)`>MfEqb8I~SHCh}$zDUjE#~pZw#%K`%&Z>nTgMn67R{oJ> zm!uu1Y|viPv3W^0N)lL98%j#qb$a|t5xSHDc7KRi*nt7X!JaJMQWSR8;UlG%J^d&M zI#J*@%brfNqElsO(cm8CG9Pw$O2sv|$w($F-TNDtX2hlPKP~K-0@ZkNySk8VPiT6N zVrw!hdYIj8$i-=SQjc>(lFPJg)-0mI3bLU$sB+-~5*>ET1dm#jRF3t`BDSI1Y|Yi` z1qA);u(+Yh;+oZgULC04_&M9UwXPFf{xcpA*+r(tMh5#=4n%8<5UukGzR$;8-hs-{ zVXjPL*=fg-H5Ob9HL&SN6mRP1z#JwvO>)48aQ#j8Zxpo+cN15vM`ZKc>`UV6<|+1G z!bsx?d!yRSMIh`V4veAo{ujCOVVroNm9xSBoMo}P$<93gJk-c-x&>wHRm-<(F0$l7 zSF>CAxBX3Y6BYY?V#h;f;)MmVySa+_i`8Oy`y|nl2t4wW%;)ojHMW!ZlQT%{T8H1a zSdrTK7tbEm9IG(X_9cHjArialiO=m3=h_ENBHj5z0iWp6bNJU=#Jl^#GNo{3|1w=f zQ>+LtOs~SpcnIFO%HOy&!hyYUFK4%Ek~Qd^wuqDQAtSwv!x;x!y2t2seAgW(6)guE zdDtNi9f-(u17G(})2~PzY^8S0A6%x>IRDaiDn@=^9m&|%*I|C6H22*}O!c)*)Yjyj*0m*OG+Q{0q>WKY+W9cr!XUX?zUOYE6XE3B33Igp;Jof4be5ePpv0_w= zT+e%@C#RxS0SPowf|}7Z4`syV(LML@Q&EJ|Z}QV6;`{U|BVE97<{HOBB7Q1i`WL`; BDxUxV diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltweb.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltweb.ncs index 2905142ff87c4b928ac6f510442e3829ef95f3cd..67c36716edb593a452da8b35ce250f128d2737ed 100644 GIT binary patch delta 1055 zcmZ8gZERCz6u#&5_G3V$unwUgYkSuLTR(B&T*n5sI1|PC!6XqyMZ?rCq^l-dm+2@X zM%kR`1Ig5(aiB0vfaP5vL=$0VCN7hODFKraSQbt8V~F1bG4tNrnGo-fbI*Iu^PclQ z&-Y_mAbM{J3H;(9F!tO-c4h>gSV9 z+D|s9hV;z$l5CVD7(_GoFJ^BSCJx$B1D4SA2?TW$!1Ik zoS1f+<&rA1S55h79~`bu_LvcNyTy>pWTZ)q_^xtsr}#qo&BBXzqCXJh!nnFs;lg4p z2)cN|2}FVc`WcT0{k-rfb_5Sue2vBG(Moy{4Yio3HNW%-Y|9)dP^>r~QY_vcJ@}X& zToNAQ!g{rI844mzTvjI9BBjLk!*JZOModJ@nU{YAYsK3fxe8ji2oI4Frz)!1d{N-gUk1+!WM~nRUYE-7(%f& zj_kqmDmV7joBUUF6&pzxc51HIW<5@y!P~e z2uaQ-bjxbGnRS?d435N-JpR7MRoJTS%->zY(wZAQ_AK6bAcBW$ z2P_xg(gc(gJRSFm@S4AfN1sGQrMCKR7xDR+*c6X2k7Py1x?bYpjUrMvAzOxC(n(y$ zrBD1~_5Ec;9(?NvWAFN3dGH^#_zhw;;1K(hUgFbVi!UF1%|ugT{L%P){;W+sP@$;U zS|^*A>WnNnqrR>AJ~Mh=808i1;`nSNA+|Rrjnsv+O}*5MOeRB7 zOlE4h^GBh)n2_mrG&B#;bvRp+v<0b_U+5&B$lgb89LiSG99+(BGVgb5jQg;*bps`^ zw^b=Fy=4w&ry(!BDriC$XIn2b<9Qr=xr=P5edQ}!4{rC<-&ocT^5gUNm2?c(+WqUk zA#J>_NwQPY#w>5OcI45Urb1HCrY^O2VACT@Zf&hzYysnu?MG=?P;S&fuj1U!QSL}% z#mDcVde_!DyVH8LxgiOeuEiMK)yHoh6x&|k?BZ?rV5GO3(^k=N;JA^_;P$?sxc;V? I>DyuY7ts$u=>Px# delta 948 zcmYjQe`r%z6n^LArFpJ8v!q$tCMGYhDQcP@-O}2*(%)05ve*%Ht~i;nmC6{RX_b{&Xgw4#h{TH}wQwobCKvC0@@lNGiibT6+HcHtiG{l3fj zzI*PucYos4kGv$-5?41r7p`oUq;si}C5NaebL2DfpVUkynxCJ4EXiI;A1>LXM>4Q` zl5CbFu;}MZF?%y3KNMM8HG_I6$sYZj?+aX7?ZDeE2WwLpaH;IA!Kbcj)>63V(qwI` zMKxg01*q-=>eKXnX64qjG#)QKH(K zEUMvc3!C!pT08!4!8(QNUGJw z!g#cn3%leLcINLcVp07Z-)=-#EP|?r^E}ller@nD->8MP zGa#y(EyQ=6qOK*vJh@49uQ^J5HL7zCxSdW>zwUFHziJb+8{&kVErP_$kNEPmo^FnE zc*9&OQX5rLE{z)1{2SJ0zqr=cULex1-hY};PhhawE|MK3*x>D@i-6GRNWD@7;H+1yIESp?Xz8mppuN!xJvy{P!J?qJdM|)~WMxd|V^5HW5 zJ*80$_r<9WxjwCUL0>XlkP#)bL@g%a-J4_nKpaY@=_$OQyi98mAMn#GdIvxrEKXI? z7@AW4`Yh%Dz#o#lpuQ0EI#gn)y%~~%R(&h$(`q30Cpyw1!dU7I-4I&_vnEPo#l9>b zm;enqVckE&!xQ*t|2O>Jthh4tx|_QXqwLs6+_O!5dF-5-CeeNJ7iDwY=a~J@0khIPkpKVy diff --git a/_content/_hak/rune_prc8_top/nw_s1_bltwisdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_bltwisdr.ncs index cb60321736669432438d270c848c166cbf2419b7..93d66dc380348e0385751510d323cd66e811646b 100644 GIT binary patch delta 1120 zcmZ8ge@v8h7=NGV-h1B@Lk{o2@P57bgBI}KodiVC1iizY2{$&B;+mzI&bZZqY;t5F zD{QHF2+!QGiLukEU}5nkt+h3+nUl2)t7Q$BR9m?fMnPj#yWaObT(0+z=Y4*BzR&Y{ zp6~Zzleqs!Ob-R?r20bNiU7xrr1hltLd`n^-HZ1-zKDy7*n-&QrGks<$LH8z-WD7}Fk$0Dgk`i*l zVXCoqp+lrCHXEC>3VQf~lFli#cd^^9KMcC;R|M)P$faYlKt_29ngU7=mt?Ywl1Axq z)TwmVZP)1h%<*=GTjn`>S}uUw6qh}?beKlvQwC>5jku4}R&T5LtE%S?(WG~sK{~1# z4T`axs(qJqrVXi*W&Re-%VWd-TtM?_s(GN2nG5p~?|*|<6?LP3ipGoTQGB2BiaXG6 zrZdII(HEuKl3Db|*xCp00hAkQW9cz07^B(JlUU%Q{)Y}={^6d1ELK_O0Q6m>&E>n% ze~6wav&lBChnw0+BcM(B)?!IWHF}!o70%op2u*L3sA;=EXCdRMwaOig>a_!$qE7HU zzv{w&*i=B1sDyW+rosX;(DXl~Ue7^!s(cDVH_Age@w(FPGFJ7l3Gs^?)Ua|MuWh9d zYW&n$F>1JeP#q%YqKi=z@Hjv5UkG#tpF12X>IWnH~u;}_c07tLIN0+OA$B+I|YRv=oT9&iVLlPJ+e6oPu zyxPO8wP68nv5fja2AxT_bGc&L9snbY#~k%u%Q{1Y4-TbsEl7ckEHH(YLWu zNTa1~VX@z)_CH81ZFLZ&%WW=u?p@2WurhaU2!cF=7rsNJ9rXQ+aWGQzOCLium9%@{ zFABF4cxbA<7|xP=kEiMis5|%@$6Gjc7gJI#2M(=hqnDG7imAR2qvpxEyR>r6i;(v2 z8HO=d+;LU6=;bU)%Q!>hx-4>bj^KeP8DCpQzjXeTwm+(6UhL)Q(7tbt?0*=vO ADF6Tf delta 896 zcmXw24QNwW6u#%=rA?KFnlz1?G>Ol2+Vmx9NnNJ3wKkiM>bkOE#&ik=r&Ej8A6D1; zlZ~m&pE&)*DFdmt2)5!3w}UZpZRZ#aDRtAiwJKeqsEGInMW~MM<%t*WchCK}_xld_ zoV$ONP+_?onEtZaYkVp_)uIF){H{C|;otmtxY94DKJHn1uk# zk2rf8d(%h$&BtdhE6=!uwrmH!b2(TYM45XAMRCJ5ht(t|yH!>{Mp$1wKO*iv$ew^4 zanCeU7R>6SoWk!7eRkrTfJ081`L~4~XYrt99@{?AFS~Lc3dN%*U|Wb<&CPj(_*5$) z+fl6motl-W`~gbX(XSn|_)?j3Y7f>0o3ryWvA-X~+H^#Va$yar7H^M1?$AjW;-O#J z)~p*9qbA(MvqoiixQN((Ro*IH$Cg>kjkN4&de?!5$Tkk0!_%Dw*km>1S)_uqZX;Zt zEKcy)2zuP8>Xh zHS=0Iv;@5sb$q8eqb}c=pU-^bC(N%J=j+utRTswE1>HQjUKUn+n6FjH%9=9bn@-sj z3p4jGlc(P~LzAvXURMmhm|t#Q{DZ>3)yvsS<3umf&>BFgOF`#*&-luaE;>%bIyqg+ z_kF~D?eeesud-y{hqtVJz8C4Kc9~wYNU@}h2e3>=n>!!&4L5m6U}Q%DE;m+VVWXe_ z^kQve6%W3hVddqi=h$+oRAh3;dtxtkv1-k%BOgUd3iXF_ZkDb(r~>L}XWr zCgGP|S7;IT?)K9dx^{y+Sdc8IbJ&*jR}E06m!Am5B{Dtz3NuDD&9GY?6k49XRrHx> zF!t9*B8PB0d4aCU178f7D1{Y!2KZnvM!t3;w)Y+%=!LfLciulHpYL1g=8eaZ=s3Ym htK^f8i)QM_>CU^BgwOax4vYMusFjMM z%sPP2d^#IxT=iA4xe2CUW32=W{S9o+#dCgLHD|gtiD%Jf9QAMHkQ-l>21UBm<)rU0 z82p*FWum+CcMq37j&-^hT4fQ|hfCNSkm-RLKjn-{F5~@h1s89^*WoHIKMr@q;F1>+ zi|q8=I^QbA?8lkNfJZ-SZNlquST9FkwAQ2d+hC3j#-roxjs5HMZdHb=u{Y)wP1Qxj zzI7r|Gszr1kD`hImen?~`yjU0uH|R}jJh2h>Bfn=OB_y%vDr5f-?kK|=e)`05v1-I zVKV?<{RTFMUd?*Nf%*_}ZiiC3_)BPucvwz=qnSkk)GO^?am7K<+yFHp&a zQ0H$nXaSgA-83xsUhL?K^4Y`JU|bkW5>6UI=Be#;48CNV+VE2HULIZ`jwPoYoc$2v zJqBmrz>hs4DntJAeH6sl@&vtrTUK18{h(B{BrT~%%ER-i#qMoEtJAyieX5n>xT{yM zG*y>{ZfIt%N3!D-S4F1xTjtm9!6U1Ns1S)ZKPFf0rWWkX81xrD&VUSb_0`im*xYB# zKVeVIuh%CgH#e3gg>2)eV@ge-Wg`80ubZ~w+*?0*I_|W8tw~T;$BOLD KV*2exu73ccM>D|y delta 832 zcmX|~ovuAk%H;ber?D&E>6B3@U>ZO{*nHLHF!}7PuLyL>J5MVMXIH zGKwxUEDMT!`$MvO85yNUH%cjmD7p}Ku`;Cf?X&vhy`10SoafBC|FH${v997!bcY$C` zBQD#bkeR1LO@d7uTd%;`3T#Vgqlm))TFuTilyHUH$Hp_#pX9E@UqlWc!lF%Bkus6g zJ@BP$Ci`7TsSRY0Lwo7~jY;I!$AvPAH=|?xQIe|RPHUvNr!b_~Qe3|Ggr2vgTNv4Q z;CaS+a^xT_^EY*E=V=pWiF7|5@2975KIt)0dkVZcdVX}uXOV8(xi_azq-#06BEN{S zek=+$;pLG~95-n2z2FO7?vN`7sr8YZiQ@hlRz{Z&@>th4HP_7^QnMSf?QkBmAXn75 zMU^}UEsEulZd&q;^#)Q{Rb0yacvPH4ebQf$%~vjU1+gt?Deggw1~|)$*kn|f+afKBFG0E4@IOdk*(!du{1wrGojA5;9}7Wp#VwYLFBK9SfUy$H zjI)*LtO>o9QdXz3l>Psfa@a&bh>cd{mL~~`Bjr$(&q#%Y{}U2|8Kza8V_p15Ri{ej z)?4A7X~OGu&9t`;#oLTHzy58=o;s!5h$KNqk!ab_M78aF*~W#@8TmS$7 diff --git a/_content/_hak/rune_prc8_top/nw_s1_conecold.ncs b/_content/_hak/rune_prc8_top/nw_s1_conecold.ncs index fa1c7b2ea738d11b4c54f01a1d1039c2bf21b154..c391d7cf50d1c960eaa3dec7885bf05a581bc770 100644 GIT binary patch delta 1003 zcmZ9Ldu$X{6vpSAoqf<+ZE1J6?RLAvIOFcNU0blFYSkE)fGKrNtgVq~TO_!Rg)Pgr zO$B2h7+;VQ^GT`!)P4vYMusFjMM z%sPP2d^#IxT=iA4xe2CUW32=W{S9o+#dCgLHD|gtiD%Jf9QAMHkQ-l>21UBm<)rU0 z82p*FWum+CcMq37j&-^hT4fQ|hfCNSkm-RLKjn-{F5~@h1s89^*WoHIKMr@q;F1>+ zi|q8=I^QbA?8lkNfJZ-SZNlquST9FkwAQ2d+hC3j#-roxjs5HMZdHb=u{Y)wP1Qxj zzI7r|Gszr1kD`hImen?~`yjU0uH|R}jJh2h>Bfn=OB_y%vDr5f-?kK|=e)`05v1-I zVKV?<{RTFMUd?*Nf%*_}ZiiC3_)BPucvwz=qnSkk)GO^?am7K<+yFHp&a zQ0H$nXaSgA-83xsUhL?K^4Y`JU|bkW5>6UI=Be#;48CNV+VE2HULIZ`jwPoYoc$2v zJqBmrz>hs4DntJAeH6sl@&vtrTUK18{h(B{BrT~%%ER-i#qMoEtJAyieX5n>xT{yM zG*y>{ZfIt%N3!D-S4F1xTjtm9!6U1Ns1S)ZKPFf0rWWkX81xrD&VUSb_0`im*xYB# zKVeVIuh%CgH#e3gg>2)eV@ge-Wg`80ubZ~w+*?0*I_|W8tw~T;$BOLD KV*2exu73ccM>D|y delta 832 zcmX|~ovuAk%H;ber?D&E>6B3@U>ZO{*nHLHF!}7PuLyL>J5MVMXIH zGKwxUEDMT!`$MvO85yNUH%cjmD7p}Ku`;Cf?X&vhy`10SoafBC|FH${v997!bcY$C` zBQD#bkeR1LO@d7uTd%;`3T#Vgqlm))TFuTilyHUH$Hp_#pX9E@UqlWc!lF%Bkus6g zJ@BP$Ci`7TsSRY0Lwo7~jY;I!$AvPAH=|?xQIe|RPHUvNr!b_~Qe3|Ggr2vgTNv4Q z;CaS+a^xT_^EY*E=V=pWiF7|5@2975KIt)0dkVZcdVX}uXOV8(xi_azq-#06BEN{S zek=+$;pLG~95-n2z2FO7?vN`7sr8YZiQ@hlRz{Z&@>th4HP_7^QnMSf?QkBmAXn75 zMU^}UEsEulZd&q;^#)Q{Rb0yacvPH4ebQf$%~vjU1+gt?Deggw1~|)$*kn|f+afKBFG0E4@IOdk*(!du{1wrGojA5;9}7Wp#VwYLFBK9SfUy$H zjI)*LtO>o9QdXz3l>Psfa@a&bh>cd{mL~~`Bjr$(&q#%Y{}U2|8Kza8V_p15Ri{ej z)?4A7X~OGu&9t`;#oLTHzy58=o;s!5h$KNqk!ab_M78aF*~W#@8TmS$7 diff --git a/_content/_hak/rune_prc8_top/nw_s1_coneelec.ncs b/_content/_hak/rune_prc8_top/nw_s1_coneelec.ncs index 1f2cad0ce6657791af52462a931f11dc476a0cf8..d3cd823c01a7e354cf4e15036c2c5714f10c379d 100644 GIT binary patch delta 1015 zcmZ8gTWnNS6g_*-ow-BH#6mkvAI!|1>0CO~+6Q%75~ykIR84CcV;dh;Ybl6rl)kXj zkU|8b(Fj=7g&GK8CmK^iY-3In1*F#0D36aC5WZpHA&$mB_SE}Wn?%wV;LxY|Vk9r8IqaZ?g@s>*5)eW} zhTf|6;zEfVQ$7#t`!M6vSj$7kSHpTOeEDHTU#@Bf9HO_;mA{vRs=Sh4?4*lW7Wj>| z&60wDxH9m-Qko4R2x7Mx=O!fZ~3BZ9+G8)T*h*? zOebFPBDyN-<*M?N%&|-8_N>6{N+)hru4DB$)>ZA|m<@$Ca=`NUr`-MW_e_zC&r`j=gFS>9;hq*90BVXho{;yz`G%WBGGSj zA-?bWhPIhS%xvgY-?eL4sPv+-yO=YdEtV4URCm%zW7swBMX2X>`WjO`L-a60y?66F zAIS~9Hyxb*98YbDgwqGCU9z$@RSP?;JCv}D{N28?@dA9XWt#lx=u6TonCQDkpWtAB zi-G*yzlO5lA9!3n>^J>CiWdgjDS`6?;ef8VOvu{Xj<$8iK~O@GO9wA7@2|s^tz(pf z9na5ED;kC)^ba-98<~oaxA3a zc4^}@moVN)P10FeKQiy2jksfUp7$mY+Udjf(M!B1fz0+ZynCBGwxe?;_ZP^l0|P1@ X!sz%f?#}ltM{eL?IxKgmJ6!()I4&i0 delta 761 zcmYk4TS!!K5XR5U9(T9Pf~9uUa?iGVbl25VOH&X_(geF~g;?25U7{O#3*G2qMsx!$ zvd>U@kX*|y4_*9Agk~B_Wl35=FG(gr6j5}23Db6$9(ovt`7z8lA2S^<_}vhjTv>UR zbxHFRmrBx=cehPT6jqM23$40G)FXt zF2(|fO^q~IRG)&hfpxM@l5)5IuRF6Hl@2FIe8NSC#v#}7&~e3R-QizJYeH45$2j++ zzldprIW`g|XM%BVQvl@qz-$yc`#E&YKgm$I;>tO;TlB_76NgNxk-1QR0DZjE3d+UyT^Zc?U0mKZuH#l2 zttA?J-(a9bB@;fDbkGbmm%8WxK9_!@+ZZidP70jmsU*W)zR}Pa<-b@f`pWYt14@OJ zRAg4nBAr-YG0J@YB+@F+&{RCC?4umiR%!GLx2iy9WP6h78uoiM8bY%tLW>AY+BQ#$ z5B5!zeGwcWA^u;Gq##L}LKyShpkZ-#-;mDdwdmjwpKd_Xp$V?@rRo8$9T4NyTjp@t eZn5b?nSrk2Y0G=#)~|uL`i)Ost9b1#(EkN*<^ePS diff --git a/_content/_hak/rune_prc8_top/nw_s1_conesonic.ncs b/_content/_hak/rune_prc8_top/nw_s1_conesonic.ncs index 4769d6a13bb7c5f06442e4571d5e138b4fc60bbf..010f94f089b638523ab5a668bc2346711a652290 100644 GIT binary patch delta 959 zcmZ8gYiv|S6rOYT?t?<(hIV(`Zo7NC-OKJ$>K5oDu~LC0sI@GFhF4K35@}=G?Sqz7 zFo0+z3bujI21ro4A%XIz4GalTj4Ua^0tpq3{y{^D@j*-o{edQyQ18846XPUv&dm3H z$v5ZB%=J6mf0r}ku{w4A!sknu3o+P#E_1-m>JWzAJ}$h3ad&`&9oSm#g2Urttq*>W z%4#;2dCFO9}J-EN`j>FM8v;?u#bK71dV&v|2@aFDYPAQV31C|Y4q zS~q?O?{oxi7@N=s*c;A6A`*23x=mn_4tSA@TxVCwjAb^4=eKh)%fe!f*!@0IB`?VX z3$8FnenYatjr!soDhmth-NZM6Ax#rwsf(BilXm@S?UB* zn=7}xtjjJ~O_|V_KPrl{H1)4SC~3lstkXN$T+)nkdHesAR;!>F(xzz*)>J&>@j_jhl}BsjQ0#%iUp30ZuhbD`9myz$v*rn3nMATGii+26@yMS? zGV)}8tx9}(L{_i*&_)mC=DKZm+Jw6qS@PTZm5Re=p2{&B*Ydrn+VTzc7##c!P4=T{ z3Jp;gPBj$q#FsNvO!~IQt<;MLCtcXT{ZslH&c+T}hy9I9_-L)X*7(%I$*Z{99OUG2 z{L<{FTv%H!Pyt3-;`9+_wNBDSOtseN$jU?|WnfRD-u`)k(crVVpQxi47Pkd_nqo5` z^Bn1zZjjz?iYcUOm+>?EjiFVZlSl({_Z#*iecHVXM9jk qyKnL*J#x-_Z{%|#S2iDOv(pLOJNc{Q&1&6 delta 779 zcmYjPSxA&&6#eg=-tZLLkTEaS6CrQ3_{~J#X%dD_v(ffB z2C{pw+m(sN9FNh~5htpm#3c0R3~=hAf0n7Zo*U$xJHnQiMQlEYes`%jnBT|j3SslS zH2m-;pw=^s^>1+6vxl97uoQH&(}DX1qnw>DZcUp&oMpk_^fRorqJBmXJ6ww~2k^XT2fMs*7ysdB`-OYvLYW^Iiq;aH&iK$1k2eOJUlUhL-^e`R6m8xQ zGT-%zMqee-j}xXmd@CR2+fUJ2;z3HqBkp^C!jvg~Ral8{of4D$`}D$4HKwO~7&fH{ zWqFxQjaaerC%wnwx*Zh3le#(lL5qTFv3iw1f%YQ2K16+}s^3gLJgA?|9mj=!!m0m0)OKO-N4*HIVorh6qAhmfn|nMQ)2Jxh8C=(+1MVm zlZvij8tFuD@E!BXgXr9PluS5Mt)O(<6)MBmZ5oXtxd~*!`KCg;gjY=(4I$E$p=HF? z*0`noq^PPKouoLE5-WowB}mdZ!u;lI^h}I259y*g@3jo^k#^WRI+3??t?6)kY@f!v uCDiRtM|kHYZhI(dcGXPgz(#SqGhm>L$nW}StUVU**>6;Y?4l&JRR0$dmjbx} diff --git a/_content/_hak/rune_prc8_top/nw_s1_dragfear.ncs b/_content/_hak/rune_prc8_top/nw_s1_dragfear.ncs index 53d660db68206072aa4b98b61ae87eb9ddc5ec5a..fb1ce167b959879ee27699df0000a340473433cf 100644 GIT binary patch delta 965 zcmZ8fe{54#6u#%Y*B?3=Th}tWZtGk7x`ju(wu^2Cqc|!sh}ohDG2@590SnXEZDv~) zryKYq8OSm}W{d&>mjDY3##|BvA^}}Cn8am?A|y3vOcegWkruqRo864`z z4wuH78wXquai|*80UxGa4mIR*Xr@#~gD5W8!+r;1?h-j(V7Jjv_ zg&XYHTW-Vkc3Nfz`Y9yGYv9FJ{?Y9wKEy zuLto8w>(|(46~8Ke50S^<2by?hqJ~b>^O}(Mi0ks;nl@M9IJyr{5waVk_#da5LYIk zS03cZ5JoGHaQH4>jPB;}OXykek-0HHaqKvv@g2_icB_&*IJ~9MD6#MlLbVC;0%Zyn zH*++5w)A7FYQ`dK!N8`P=T@4wlQzpHOqf zT9Qqg^`dG)q_Ku4H;gNR{3(u#2)EidRgW1L3u0jH1Sxi ztX*D4eo#zrxo($cXrya{np*J)Z9bYb9Z%rYOcq6qA8lWVi}}{8C`aHMG1L;XC7HZRvPg- zEV?x_{|Iy}3@Hc`D@apX!V(sRE&@eLP%!Q1Rx~if%;!6NhIw!FJvLa-G2NV{-yR+s zmdtTo4c(553e=6#YNZzSA8G44HLX`Vq2VHBD2*ueP>oWLx){2lj7DK6&G>rKj8Y~M zqGftpr0pXw)j8A}DNOCnhE!5JkkYW*H0n;XXYN1byry1C2fWlY6NC5Ci_i`XdQ3&8vltY} z7EDoqrUEjB*I<}HO%Y2t&RA1qh6F7KDQTmL*~h*^9Lr(WIH?`J$tG)@3)RMIm@4U! z1YeelcU+h?B>`|q%e1LJ0E;48z(F8wst3#7qz4=rWEE*Ar7Smd!U-nnxk5#b8adm& zFHr(5bS-xlKX5jBJFgJObY!2Oh~p`LiJ9HXPq%|cnm&9N`su=v5=bMyC;=~Jv*aSP z1RUv9aW5z+z9b#0=}gHibkniY&7h&7(io7?a_KJF6>qWLTpR5#%Yp>zE%SBLt@MrK zY%BcP+i_s#+0Qa7;$;h2%3EL+0%G727$Yn1>a6kp{oxZBiu zAzZNYicg%*Hlq3`SmnIRDm_nvdU^WF2EbMD()+xaL&uPD!Q#K~QZ<5KaNM@T;xCB@6AANd*$8EX%wOX1gsUpHqdDUt z8<$l5Tr_1i@}#|TATx(3CP!x1GUt86cE=H(T8hu5HeAir;dRbp^G&RA?&WYjqRw+1 zdW3zhuRNtk?s1PIF1UvSo(?YXBF}rCC+|cw+kp?>2F`zvGcAVP=5rH=bJ6DC&Ea=w z^*OP)$P|cbLf5oM?oTs!6R=h`33iSS%Jrbm`2s3lyzOPtY{w_6Z@1?*3(H zBAH08P^EWp!lggV;>Q1&x~@@SQZ&ht;{uXD;~oyh7oqyCBHeYlH)PAViP~VfzFCK{JQTC5|MLOlcjLHp~@y&j?s1NOV-MGM{$Fr@x3;bvZ>)zQN}T3R4{V zXUt^xY8Mm{^6-WzQ!BRAw2&1)YaY-d+^Y@HFa~Qu0qm-qMptm9E>QAB{c$YzNfUNW z*&oR}u14;JX5C~h-x?QE$C^O0{#91{9Kje{f0>@jyY)RO)Qr}K9`0(#y2f8Dm1%my l*PqG!t!o^#SsrX@Fw-dvw+&iqT#1kU4tu*#rnav%{RS-q^4S0Y delta 659 zcmW-dT}YF06vp5G*}IRN%4~bLIk&yroGX{Q4HQ8!G^OYRFA_APX6Uw58(qLbXsxFzPZotP2*R1n09=pwlsDs>ro7f_-0h+eLBrDf++ z46Cn`)6Wf>d^OXcRKKp%h$=x4$b#2KiC1SX=*aRHxl#q0UMlW97$hy3lDHt*$r!|# zQ7i~=)~=JPtw>228MMUB~ppe3j^Z?kV+;XDjm-jeBq zl3$2~wTJwbeAVh>%)>Kmancp0(#IHaxlF=Mdmn`aqylC}FnZv{q!JxYW2oUj!tY`e{zaucp1USf@}Uzj~}*&4w{|a-6-u zxp0CV#E)MHuoO;34>1cKM$4Iq)#!2Um_unom_S{u zfmIc6iw)LLFdEyz3}sQ`l0JS;(Ea z-rRIpq*y32D50V;qXbn;X+O z{Eo_&`O6F%S&U0i0zvT3j3xe|ak5QO;>V&G3?`Z>i8C8j=fdM&Bm6*($dwMb~Jd}d$%WIoFtl=nfX%_Ax+t9Y-5`(gZ!q6LQT^a zo&k{`&v|krE828-D<_hTE2%9&fLYZ(6Qr;Ii>_l6RE`2!;JNBO4w93&Nq+<`DKy+YZn*P!YBx!TxBx#c^n9-OrbfyWOe<2pjKbNxm`G*$2d5G z3L_VTispY7=kzySMQbwEnayY~Y;i@-W)_Q{#FQSyx;!t8aKsg9w!q~I3?UUB;y}r) z-cz&$8;b(+TVoz^-a$FBu#P#_it1<=Cx+4O%6lnlhcNGC=O8M~4lbR*hh`s_-itlO z)4Xhxd~lJ6cxe)QZ|UWdK6n=&<@i;+yZ8l;KZMtp2jtdRn7H&fT9>qQq7MJW^tf)d z^?Oa&HLZcHc3hroIE=6=U3*K2jJZz1%r*m))eeMf+>cw-dsI}v-EuYMUKB!2~d zQAkjJP+^d3D4vzYrn0E?CTkpY`JHvio2X4)L9X0dy;e{$dTXcn!eMl8Kfs?4z`J85 ze{)f-w#dF6Rd(vcm0fSpFnV^U=>a%XckwI3a&0OpXg;2=>n9zn>Z|A(bkOIMWy@G=(E`<{R3X?UL#_C561wc!~Mt&+z5GPV!-5 zeLmWnPSAZQPDkk~D$}4SE~U%pZTR;`*Nj@-oV@fSd_#6Qa)*)sF|99wce8T=PdYnFy=d@*! z4IbCOx%y5;HaoJ%K&@93rP^P*jLzyq1wFa}3H|P31w^oF=eZ~CbD0+Qi zF8l{E!-dmknd@s@@O@@yq~Mu7)Et$Q5Ha71}LVga{utXyVPVPF&N8trgoi z62xTX9NiX^Rm&B=JcwPf8qxafugt$3Mr5H|MC)!TeDO;WSvalm#F%is*h>7<8f?t* z!vBJbRO4Mf-)J7Y%Re@mXptCLY!IKz5kJKHY~q_`SDZABJ$9Eku%cPXPFWYSV|&<* zhpR60l$6=SwbflbsiU>Mfq#GL5fT@Fx3^@`4%BxXp);83NYYZg(i!Fbs@UBbSIB|% z`zI+Med}AYd*|(f1sjSnyFN`LIJaRLd61W=Ck0CqZ)NZGng1aj#ZaP+qIi%96zG-~ z$(m_?oh4CR7SRu`GWQq4pG;9MhLdNh8OcpSn#C8JKz@X~pQjIxOu5NNj_i*W80MSEEkv{mc F?SIa86=MJZ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazecharm.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazecharm.ncs index fafca7d0c249e5a32c1802408ba6c0f15d8eb875..af690f7f9245368da2ab420f0b1a65bf39303c5f 100644 GIT binary patch delta 1012 zcmZ8gZERFU6n*#ZY`bkOyRo}1?RNWK-|kx4?v`%TN>b?-`U)B`H4REs3X~sGs?Y`5 zHX!{`zl|cKIVmB;uL@CD6399vNQ{XewN)fSg9<89s!?fB(;89Jn%4K;N+kZ6nLFp6 znKN@{Zt8QMInViR#%gU%tfsa_QI5={^Mis9pO$rEE?3u%63x%g|D_1OVvc{agB=bF z`CSoZilUUi1tKptxfgI~JNn#J9Nvd=kH(r8Bkl$c-;7ze&fy!OdURHYV0iYhnk~mY zK_`8WtG-`zwEKm_+=+`YQ09|Vbl$}aJMfj-$UeWh*q`%rM#}pq8nsd`*oS+xDlYj5 zQ)(d&Y75x=6yDHYa|K(i1BH8WZ+V|fAGIbGkKv3~f{NmN%$4gdz1IfoOwbQQA7NkA z)Tu;Wa2r>%JRA%Y7d|3C33aopYNvgw0;HRKXs<|eYz*m!LUeXJ@m<9&oP84Nq5-b` z1;dLDaqVqbS$UNgx67ug8;ENRtg3#Iqla*%`c;n1VmMriW03?$dZZSuC$2q$H8s0A z){XXPAQG@0y`%`IqC7~p?qr^prK#o>l-Gr@hHDhWI!e&o;ooqpNj~nh$%+|X6hbU} z_di}HVfSpN``8ZYUuMg;|1q6Tg&H%`84k|X%<+XUJ=H4b%MP0SiHVqk#`QLp**0S32W-;iAopZya>e zUYy9*Pv~V~k!3;G*_Ls`^p*@E&nCWQ9(o4phxU>O%XWM~cR(aVG>gh4 zD1>*C^>hrsB}1(r+XXXqtza@`W66xX}_!4H-s8cpz#%@qlVpr}-ok}3pIgQ6cQqKE-gs)nSq8}i{k&hK!~?{e?? z{Fu82xM*2ymA>}t%INo!)ZLXX@+;iYBxWh2`G)q8Xl!ikKS}XO);hv6%W{T%loYQd zN%}Jo6=R3P&i>!v*rwu`LuIoUKBteq!yQKzo9pq#VX#>R&1tZ=4>9L)_6Eg6r$2{s zF|5AN(~c;zRgB$e@v4H{k8SL3##3!B7tgZ}d-C3m+losuS1;qQda+Iqa>-kK(5B$D zZf93Fy7lumV`t`0W-HeF8g0HenP5gI`t?$jmlR>tXV`qLS-8f69>k10T)f=!1Tf2= zHRQRto!e0GS;d?)dp_-M%e|nKF2q78!mqtKU5u93ZpYM_dOU1#3 zI`Qqh;!OPKTyb>ipq*ZVqAqb@Q(U1GZfzanr&5NXxw8PN#2n-&On#(U85Ii?0e-Sz zA}dmfUvlU$)Lrd#9eum@(jo-ws=2jN)YZilD#E4RS7|a5dzR4wr1uO_8XfhEWjE(y zMS}}+(jWyhk{c%dKFxZ4sRO?zS5Y;dC5@?C_LHUTOym0;iDI(w?0vy}Y5*hqQj`y8 ztsCw8FH#(}4JHj^e*?%1TjOl%LUp4#uRq(7A0|3-ye~5{D#WjUqlu*W^9Y3k+FD@2>!C_MN&|HP|owSo!>JuOqA9_r!~ZgKO_ i<}yz93GHhWMj>W8VLu3=)L^ diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazeconfu.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazeconfu.ncs index b06cabc2dbdaee1c8c5553a4c6ab89f111732ad9..d632bfa202eb4bdfe2ceb2a4edbed84d3d829f4c 100644 GIT binary patch delta 966 zcmZ8ee{7Rg6#ee~-q&^rYX|Lg)2?q@%doUvyP%6iCahDLE*nb3Wp;L$Kvi7J*t!hy zj|qxDKsIumArV1^=!`fuj|7Q^h{H{AaUqJCaZ!nh4w9+F7&J?$Ut3IwFL^Kbo^xK# zyXTFbox6&^TP!z1RtJIP2$QtmLq7gmvQ+=AabJLUdY}q4BGwd z8^9-a$YpH8o~aideQThLmYk=n?u_;@)$M+-F|m&jGr`NXxb*qPNrSBPFnJ_ z4hv!ST3&+RV>L*{jQHKA^r(@OYGyetgjo6Me=1GniQLI1-OIU>t}5#{|4(Tc8ZFNo z=QWrOUE!&3)ogW(;BTH)b<1>}&-UZ|!+zCSJ1uyk&#YalVD#}RXEsC zig=9!N9r$=2X)J;@pbeJpYp1M4aNlOATutap7>H zjUGTIv4UTjRCSL=1(jg?6GzF7+RkR$iJhI7=otEv_n63NvXmH>hl=XON4$&SIc!T-h`Wr>V zPK>O!;Gdp_^giaNy>uFpbcx@SGkiR#1squyb31ZK$+foTXB9A#=@B}k+`W@JZO81^ zNq%WBBF|rsb5H)tyZ7S6w$r?8Oug__`%RomsF{5!EA2%VaONwxBg9R>dauw5m- delta 741 zcmX9*TS$~)5dCNN-;1HHm(@kf{r}Zp({*(>YEd87qP0M=6iU008A}&^$kx;iQprmX zL21Q_lAxe8(;&gV3c<1mlWv4130YFuLl=TT!iONVUmKW*bIx#vGjr)Zk56;d_P`Fc za$T->vn1UaACB_N+}kWl<;7L3yhW+i!Rkj4+0bT<=Cn(=V0E!}509)WD|z^2^|7`G zUUfCRF=kownbj4^F@ma?bL?=TJ~l;IVogRe!J7Dsl`~=~@wdfbKV`xNB^^Vm391^+ zwnE(`O-VMZilo`R?6*vkL!EKY0StjA(3EuA=_4^?!EpTq6*yV~Ob}!;k_H}mehtb{7*$;8oeU`Jc zP{7md^bPt8pl#Z*l_6{D!u&pkHLSsRck z3eS$-r&zSqZKsoXRX0z=czmpc?68FLNXDknUh|a{{lZc&ZiIG_9}A&WQsAghB!lqP ze`dZsjl{-oT7_GUR-egc5&C_zIPxIA^#zs4Js#7N%c&QJe^;W#U;do)pv3WccadXao|g|nqe+_3vG>s z*l)h0g;MrfkuJ~e%!ODKU6JjZ%`5?h3V&|0ow*&T`y zPk#-@%(?P+-O7Z8KCVCU|U;dR&p+4PcDcjSt=`MVlUg`m^*T#@B~w0%|8HjQyQ; zG=Php;naDuAImMFlnCqoJ6~-a84mQtxTy5oB;-c3v zuxW&Q_8`0UUhLca6Yt!E?k(qe$Bu~ECFi`LzfgZTC}W48 Ga{miv7bAB7 delta 757 zcmYk4dq`7p6vyv5yUrErX1ZinyLa0y)2Umf6qJUo1cOQ{MI+NJG>h7LXpotf5rvPa zuareki(XKWzbGM!!lE+7l%$vTBBTcd77?Uq+vtz}I2_K0!}og*=REJ`+Aa=X>{+Jf zPD^&rlcbBa*TY96Sm{QD(aP!@5Jj7^>^d499sMlHR!Nc^O}Lk2#%aX>uQ8mpR_rpW ztjxq2V=8O;cww~5T56~o#MU+_rh2v}h^rD66sa0VUBN!}qQPDom`Q6!s;$Cc`w}Ro zT)HzIz}>jntnb56Tn)P}p=#dEIt%MdoWnqLbuVhZZl;;UzWzrOT~gR zk4)h>u<-^(B7M_h+JS~mgLEC$-h~u{4)08o!M+utM<)fYbq;HM%P0-Ee0Eas-Z!3f z;+}cQEQM#?2GT|)O=ohlfK~{V!Poc}ORA|(N^a_h+2`a5kk`hgRMcE&w zAc+e6fh2`U(ip<-ifi;txVLxdsPS-R7dL)HcjX8-_|wiO+z=y9R258QUz->@<_)Dw W*l>2hP?#P3s6VJ{v5U%4Wa@*T!TT!T$!ljqet5S+kf*RcvwYsS@EXHVz5m+*iSwIU$rBS17 zI-?VaK5G0J%&2o)%p}Y4JDM$-3f&jcjASk>nizG&%#WC58jbb#R{!yzbKdhjzw?|o z=e-~2AAV&=MRC^W8Qh9n&;|K1U?vvvY8doJgl1yLrxvpd<}gyGdLT(i4a@4%p4 zV=WUy9phP_fJ!wRl@3MLM<|+M<?$&{3lP(yT*%^{rW22B#rFJiG2a(p4zyr{zk@?P*tskl z1^U~poWnAG6PNspfAk|ZkQp<4YnCD|fL^(3bm{SFS*teV#-q;F(7xtZ`yBop%I9bY|z(yFQa=fEo^EPAUdEYuEn&0*@n`I;tLh+yp$-hWus*M?=vLih5Mq@pHL zp?sDt86W)|48jjN0Jf?qP0!>a1Q7E4(B|F=gt%EX=mdcZ?SFR zn9S@#Nowl3YAiRf^%-gm7Ph?sQ7kSQOq_ZMr<`%rUa0{h{ah?MP8_IW|AMn@WkORg!sl z3r;FdPWXz)ipu7T=vB_??W-)3B&iKJW!a!l`5bA|o3UeLB5KS=Ot;$gDGgD$APRR` z?{nTWOp(<#$lVfpR?;w)RY8A+9cM#g)eOfXPw8V zw6mP0!M^l9&NPVnQ3hi7UmP6W$nK>W>u6?ID_%SHu*)GPZq&}RIq=%uUpvU(Q9!&8|5eF2N$D&)D~o9|>{a%W*60^f zG}AyGBav4lmRG%FZb*RFAE3eL^53HwIJ{1!KD4a^SunSHJY7J2wVEHKh%?_MIZ28* zG>{`gln|9$ks-N iZEoklVxikon#}$`!gpkij?Uv{;|E^WEpDHz)&2wAx&o;H diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazedomn.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazedomn.ncs index b390643eec7edc02f99fc7ed99a0b245d8cca077..ac24c75b6422bed041a275befdefcb78e34b67b5 100644 GIT binary patch delta 997 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 IPS1Ml|H9@Y7ytkO diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazeevil.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazeevil.ncs index 9df6b982dcf65723864e5414340881b6075e05ce..3b1140c65eaeaf16e2d9dfe2ad181b081d63666b 100644 GIT binary patch delta 936 zcmZ8eX-rgC7=7RO?kvL01cXtU`j`jIb9oHH80lhKsf@AGDDFlHBBml`ku_RKn}}_# zP0OM=(bQN#C2@&~+chqUKZqNmDA7n#6I>D#t7#=6YWjm8^v#S-OkZ+ezVDoK@44T( zqy2nkfZc`RmHL{{tekvBIg)?Iy~V})Nqpx@<+O_+O2%oIhXeKKb(w5*;BS}CdNMrj zTsA&LsrCU%-40<)bm$h9MtjlbZe_m%hdi46*kiZRGvp(EW<_tz}iHic+vyrSb9c7m7%=z@!cw)=c)@#9b}G$-D4IJL=3> z5TT5%=t||CBbHLUUurF%7ifIQgWO zwN&s>zpM$VD&N0`#9~u+&KVYb=dx9Mik=L{cAZme=hO0po|ayXs4IxT~sbR3Whz@Yz3Jlx366&CQOUo&l#_ycA^h2iU;}O zZxI#)>&yAx1>}~@<3EPqAz^vDq|ip~@NGJ*_UQ^*HhA%1QxUC3cG+jVzgyOng$1SH zY{fa!P+nO`KjD1kC|$s@sxK|%b=5}{hs^3CN2eCa_BdLrS5g>HtJ6~rVUIxX1sKc4 zN}0k!`nEh_-gg&cwe6IEU+S*XVieW~Xbg?@pa2{VbLbR4Z3s|5Ha4V~{-{A^mJ&>e zoNBFvD3bENjrA-O7+nn~>6Tp5IH=P1(7qYu_91NCIT^O5Ke=rPBTfC>7AG$?ub;|w XC*+iUwGR3nKX*Ro(mk@SYlr<`CWR7? delta 733 zcmX9*dq|UU6#eeq=VrEz%$aCz-_&o`rZsyECD2t?V1L-?Az9QaYEd>zCy`oc5g6p7 zIEC6D_AtysBKuVYQCMLmr1pm16j6p{NQv|y`?L%9k8{rDT+Y3>?(<+5n{u<4+4E*h zPn$1Erw7|jK_e>&u4FGf2n=`fo-tmb2#bu!kO46^Dm z*vBN>Rn(ZO+2+DYvsGl6^D1Hx_j9V#pGQG* z3lfx^@PvjjutEcIs8%|47T^D!rS=dzuZsb@gE;y$YLdKSd7_KiISZ9)9jEO>q$7{@ zudu~Y$?jJ4JDS<8!=dCMo^BLXQ;fu^e{pnb6Q{0)$JxRuSMb_-kW-wfsE-yEE``_~ zz=@P{PE+wKb%ch+uW3tUe%6Bad2UfQ^9l3glfviGhjC9Me4c0w%#0EH=UkS#>xwoL zEqvZg;(HrJ-CR5I-MJd~lkY5qHzH2lnXgfu<3wNfe!Y0I_?4O7iT3<_nQAe9?I68C zWXOVUe>Rr;Req$P%%8>27igKHM7RHN80~|9(U4W%yk%NbUH#Z860lBbZJyb_7=$>aE&^rDi+~E~8913KK~|Zs8=-iA{xXnHyrU zsHm1A@U-YQEka|lN<+9&46i+?!OY~O& diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazefear.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazefear.ncs index fd1e390d79c427ef58696b82d3ed06c5466bd77d..e543fbb3f4f3ae35541e409d54075aab683e99c4 100644 GIT binary patch delta 1035 zcmZ8gZERCz6u#&5-mY}ccCOu6OV``Gc7uLwodIhU71s@k%KRWKY`9orl!4Mx-4;Gn zQ2h7-Y=lS1NQ5|Nlw|~CUXrQN=#Q}tk}&y90ILwt?eSW^|Pm89cypA`gbntGCGet!NhNp?&6dV@`DGG&pgl3XT9 zlJ9d6InZjmll^IIvjsWOg^z41s}2m=mT}-7{A1HNa2I@bjlG9ZZ{Nq>JTw)0#H`({ z&_!Hx{%%#DmrZ&!E>Tff<`AZmA1u7^nS*X8)N%(DwUC`|J&=3BZ#h}VH7r*baZwkV z)gZe@;43e}5uN6E7bnzDEai3CPE_zf}Y-^3i;hI=c!xMmpX{9;U1D7aL4Kj(b`-{RdIok8#7 zUXHH9swIDL^;6=Z;2p$OajXs<<47-NLLYPZIu3>RayW_I%bX$>@e)V-v94+-*KER1 z5l`4-6m?3H6-j!LjAC5QSIw$d&s;h#%h}}?1qaxLL`(55gZ7B-aY`{vNjJ~Svb^Hk z|H!!mEdb0Uow(1hOM5g))nKNgw zFJ3LyF8!W)Vo-!)X2m-CmEn9tl5wCx6D`XJWFDCm#ScGCJp48;q=I;__D>#qPIvvA zPq*rbOZ3#KWEl+SWKk3Ii<$anGhG%_kHu}YU2i~5UKY;b~FP8keaQn_oIb9(cDj&^t(Oxk3X- zBp=n0{$z}-xR`9tJ8Imp4|SKf8gH z-Y&0qZvmsR{Un_gO2?Rq-b7~m7$ gh`08O@48cY^gdR6@QbBo(ipKhBziRQWKWa%Uua`N^8f$< delta 868 zcmXw1YfO_@82;Y(`zQs0wzgj?l(t_#I;2o2IteBY)K!SOh&rP2GIgR+a2e33HBr>X z_(P*I_0fp?u_!nYV`k_vX3_X(7cW0HhS$t%OpL~O!)(S&6!!J2{y3NSIp;jjbIy6* z4sdTjXV*pRwYg);%0HK+p5DIfPi(q>fM|Gl__d^XBx8KXGRtxj`A<@Ok|b$2LF7WC zEr$#C!LdxmE}P211diA=_E+MZt%8H|@!qC$umSDDYC)HZVe8fOUUl)R(5aR>rtT3mnAi5%FiRiLIeeO@B`8u2xY$dJUIv!Jbtf+#fxGEw>OT`i>(<(N)yN z5gAiMFS#sBOf1eK4i8~Q$&VafjHe~tTyhHg!#m`X5MkD2m#8fD6Gv8HUfBjN41 zIr@grPcn|Z;d31h@&Sp83k`K?v<2?QPCA3rjWL>ra8ng`gvB>aQH8Q`;@gwt z!1Ct0^!2H%;CXXCt~U2mAAV|?PHqggRFZ=p z)I3e1s4Tp(yUZQA7+T#$<#wLyA`_*#$;nelPdi;3}Ib-x+a!H-c*nMevz zt|_;?R0`XTQZbs{M4TUmqzpn&{Ac=G%w5+n%k8QLd#wl24GG>|h*+B&Ic*O!zb`b| zMwA-w)HVw4jlJByQ2e@aQ9j4siNFsn>2wr(4nMFiDfSy0(p&U(>hN~uh!dS(SpEmY Cj1++Y diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazegood.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazegood.ncs index 5a45f9e77c974dd4fca01b82130f6f21a1efb832..4f0ea5e93ee8f5f8759a57d22abadb302ba42940 100644 GIT binary patch delta 936 zcmZ8eX-rgC7=7RO?kvL01cXtU`j`jIb9oHH80lhKsf@AGDDFlHBBml`ku_RKn}}_# zP0OM=(bQN#C2@&~+chqUKZqNmDA7n#6I>D#t7#=6YWjm8^v#S-OkZ+ezVDoK@44T( zqy2nkfZc`RmHL{{tekvBIg)?Iy~V})Nqpx@<+O_+O2%oIhXeKKb(w5*;BS}CdNMrj zTsA&LsrCU%-40<)bm$h9MtjlbZe_m%hdi46*kiZRGvp(EW<_tz}iHic+vyrSb9c7m7%=z@!cw)=c)@#9b}G$-D4IJL=3> z5TT5%=t||CBbHLUUurF%7ifIQgWO zwN&s>zpM$VD&N0`#9~u+&KVYb=dx9Mik=L{cAZme=hO0po|ayXs4IxT~sbR3Whz@Yz3Jlx366&CQOUo&l#_ycA^h2iU;}O zZxI#)>&yAx1>}~@<3EPqAz^vDq|ip~@NGJ*_UQ^*HhA%1QxUC3cG+jVzgyOng$1SH zY{fa!P+nO`KjD1kC|$s@sxK|%b=5}{hs^3CN2eCa_BdLrS5g>HtJ6~rVUIxX1sKc4 zN}0k!`nEh_-gg&cwe6IEU+S*XVieW~Xbg?@pa2{VbLbR4Z3s|5Ha4V~{-{A^mJ&>e zoNBFvD3bENjrA-O7+nn~>6Tp5IH=P1(7qYu_91NCIT^O5Ke=rPBTfC>7AG$?ub;|w XC*+iUwGR3nKX*Ro(mk@SYlr<`CWR7? delta 733 zcmX9*dq|UU6#eeq=VrEz%$aCz-_&o`rZsyECD2t?V1L-?Az9QaYEd>zCy`oc5g6p7 zIEC6D_AtysBKuVYQCMLmr1pm16j6p{NQv|y`?L%9k8{rDT+Y3>?(<+5n{u<4+4E*h zPn$1Erw7|jK_e>&u4FGf2n=`fo-tmb2#bu!kO46^Dm z*vBN>Rn(ZO+2+DYvsGl6^D1Hx_j9V#pGQG* z3lfx^@PvjjutEcIs8%|47T^D!rS=dzuZsb@gE;y$YLdKSd7_KiISZ9)9jEO>q$7{@ zudu~Y$?jJ4JDS<8!=dCMo^BLXQ;fu^e{pnb6Q{0)$JxRuSMb_-kW-wfsE-yEE``_~ zz=@P{PE+wKb%ch+uW3tUe%6Bad2UfQ^9l3glfviGhjC9Me4c0w%#0EH=UkS#>xwoL zEqvZg;(HrJ-CR5I-MJd~lkY5qHzH2lnXgfu<3wNfe!Y0I_?4O7iT3<_nQAe9?I68C zWXOVUe>Rr;Req$P%%8>27igKHM7RHN80~|9(U4W%yk%NbUH#Z860lBbZJyb_7=$>aE&^rDi+~E~8913KK~|Zs8=-iA{xXnHyrU zsHm1A@U-YQEka|lN<+9&46i+?!OY~O& diff --git a/_content/_hak/rune_prc8_top/nw_s1_gazelaw.ncs b/_content/_hak/rune_prc8_top/nw_s1_gazelaw.ncs index d492409bc353ae6c6004c651ca227fdc4239f3e2..ddf8f304e5c4bbb96207cdfd2fb80063b30170f2 100644 GIT binary patch delta 961 zcmZ9JZ){U#6vpp4y}j*9I~UusKkT-5rFXclWvdyA5Mi#1n_#9$M%*%(e>UgFfVD8# z;+IXMm>^rsqX_8u;F5)j&ftqoj0ute8v`T|mxx&+hC~8EBQaA9#`^Y-1mcH#?|Fac zdERsK+z+St<~jD%CDt12;)~0x6lJ9Iu;&9e8)F!82e{}f5c%+>b28J;DW(xUGlTw-Ybcda=y!!HU36=fdiY z((K0hz;S zXf7Gy=p8sByEwX8ycek;EPN$^=YZ?tSH)=TE@IZ5oG#s&821S-Ls;X9gdrv#pu-+`&+u17VuCl7@e@eSu zp=ER9=QOB^rl~;G#GX-k`V9W9EE5y)@0l;16hjr7-F5yOYpZ|cR((st;^JdpsyumF zyuIQ%;)yqrjFrMsIm@SCme#-d)T=m=RUo`o202egE$3}K(NrQ@67?Eg$Bo)HaqOv? zZ0eN}d&TJUPpVXm-e0~pIPdEgds`DKsVY-8%Bq)q{s)9bp zE2%J@!LC$(Bxnukh$+#W%+2!2u}J>?qvY?&VoavSXiDtZdcj7oqh`ki9+<|@eYq&> wx#rqEopI5;m?BXgmiCVFZkM>)yS|XS4vDHgTO9Nez8Lz2UuYBi5A-I*bZAM!}c`BgP`$Wse|DrZW4aMByp^?#yl%Jte467yH-`uEu9eG~9vFRc_wC^d-@Y^Jv)S;_aR>iJqR)dKqUVnO{k{A<@Gg zUXr$nQGYS+1zMpb8u2#$7d=SVp8ulzYc(3qzpi#LLtmiCd_Lb#G7H z<@x$%o3>V=4Zq8C*h*wqM1|V@n-=_)&GrET%MkhTiZ?_pVc1jE#OAg@%Y@RGYf@BeanI;hOk{~7|#OdainFZnph9wFn3Q?ztK}@zVZ*TFB?~gaVAlW3o)VlFyU5};7rx9s9NfU&vSrv74dSv81y(D^b@|) zCfL{_E>`{J;@KN;Jun~F^m5j$P`ysSqZ+zooi!{gvLrpxBGdHr^i4^rvA~KRB#bb7 zzr+T^>TT-Bc?MT*kxB`O(C$tacJ}-uA6aBapHG$&O6SD<*1o| zo8wOlEqp(5EQPxIV;nt>f%=minM7-3KSwfneT^ncqdIXSk63KCEBT_`%s=ScJS*(C z?H?t@AxW)N=vBIPpW!Ta->)c^FBK(te|^KoWtQxm!wIj$&5n=QJ7 zZIkXPn_m8((&3Qk;bPIE2Km@szWS}m$DdO8>o>*J0$JwEU4j~%4j$bJYS2W@qoWEB z^@`sbpCKMxjO!&n{2BM4wdrrtF|{C$Txx_rH!Z>|UR`1ke|A$GS^9>PZlQTaTKHFv zmrH}G+#cCflZ6}>i9h*CDv|kcF;le zw|q!LSdrdNt8p>?7#}JZQ|XjK)o9#$j{La2wOM`3Ct2TS3SHagpk><_oyWq=lNQ&V zX`m8Z$gER8@E0chF}>ltLyOP=kuI03nyHB1QCPe_-x?7w^1skUW^ZV}Mp-e1}Ps zpf-?+-nIwn3`W{a8pa=O)nVPv_fk}fc&sFa^?7Whx|p@NAZs5p7?JkVG$O9GkIA$b y`OYzZFNfr7vw&T{@PQom>>lR*gJRj9=jL!WElwWJs&o`peLwT_vbfu~Z^pm*YbGE7 delta 826 zcmXw%dq`A46vpp4d)?hObys(9mYaL;>ZNVh&0Q%m0=H}{DoTSMR8*!#7E#(mBP}W` zFe(eaD1|7>==DcbQxQZ#h&}!gOOoh8r9=-RDWVsNcK1dDb7s!>&Ea=un69V1tD93w z;&ZhH`FYXFl5}y`m6SggZmmbY?iN|{CkLyK;c)s`djTR3V$M{~IfDE#3RXB3)~}+@ zsj*s!>ysKY3zv^^*L&$O;aUfEM$vpk)#=`EHpSc_(76=hM0a8Q7yzHK4FQbv-=_%v~%|C z%7mpiBi~i-0UOmJd9E;SzA&xe{UoZ^U1C!YL5w z=1tXz+uB9#v^`c)G5cjI^%(uTk?gUH#YLRf}!MvJ9fsin8jtR0L6-JzO=LkjzN`_DK@OE#h1CYv!Zf zc(SR999Xfrjiw<`qtkcHssZ`%re+*nfKsc+Iw|odzDTlHGS>coF#)0xCT`^zDeN** z*_bXfO7T5P!_fnQy*1 z-#OpRksCZQ!fC4_4cZG8rRB>M<@Ch4v?(`hXF+NK7k-06)z5)Ao>9ZBA3|K!Sepl^ z)vPbXHPtWlOs8hTh4c|Jvk$V*DSNW#+i4tE^B<&WTZK(g6wbzK-7QDGKe{+)J^G3k zv)g0d_H-F8`V%QeK4%?*VT3tv7=uMBb{J3ayaU*0oOaD$USMX>_aGSD>+*k>*cfl( zmXU`m!7`VBj|EhkfCrhOtL!c@(}zIeRw_nDu`avAM6A9cKPjGO4o%?c8Wpu+JI<9Z zVdn)rDBaJYG_;nT=T&7l}5*KYjUgdGthcHliiggcK7v$r$CtEns`9ZH+ ziYgy*@H+JB4i3Ewt;!qr+Guum_O2rAiV`KO^ZRo(hvqYDR?Y~KSdtV}tgn3CBCIgE z{dOx<%TJZI?Xsj!f{w24Whu3sP+%m0ZQt(jS`N){yZ@0_w6%9mLxETv$ z)3Xox;-ty{n+G16NS=&6rx9NmkcaEva?pMG<(e%{YLpr48U;BJZJMHXjJ2pJ-&l+O z4LWt>rwuiH_fi6szv1|?)p#>YoG|;j`<68;Yc+6n3qY5 zOv5Q68 zO2U6=iAWbpMbLg8#1bTZph2gR@Ss(FAtDi)h**Ma#KU)T&hPuq|196T0Y3edvT`tn8LO}klD)iS4cBE*crk+vxn7Q>@wTgRsfjuSuI1q*&(a>(e?knXMktW^qQ55q+s2V{AucgCt38!7`U3!c$(F*wTdV^jVx(tf!}()J&9&7%i3K zd!cC_PWpiR>1OQECUC+PcvmK%SqqsQ6;@qEZH3!iYf5|nH%RZopq7jW?i^EEYXq#; zK?>qC`Z#g9ULphJ33A3gvB%>gRxXIMQ@=C2JMb_o0l^d#da?`IIE0Vc`&fH~U`{7% zPAIuwI6YI0_l_raS&=;LJgbekJ*|tKFA&U3MdS1UI~Sg4SA?8rBi0VVmA{wWbI~;8 z7l#jurh;WMKgko}LW6-H=itXikN90YBy*o8hJB+l-=8ZUEGQ-V1aC<)MwCR%Sv1CX zpCMeBgKdlZ`Ob6UEty4pyG2wjIbalJt9}~kq29JxRIc~Qd;p8nR;;2zlbat_7{*0C9p2KWs1IhsS~w7O4Zk{c{%!zs#Sw9maB;miX6hjErT%)k?)eaa8e#MUAk`(0D_42V0(i$xIQTHykIrKM+ y#$`zuJTe(_eK#L{EUwpYNaeszG2u)gnl7WU?X78ZkxhRneaHEBhdAC|X8Z&7bpO5p diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlconf.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlconf.ncs index 09a038a281b42ac39e92c28b3d5f865145a2bcfa..2649dadb6c8c7da6af59824c65c629b09a8941a2 100644 GIT binary patch delta 929 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 diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlfear.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlfear.ncs index 1878ff22ae9623c88a040f115b1fe9ae9d8ade2a..1ac9f0e1ffbc1f9da011f5be23b99d71df945ae6 100644 GIT binary patch delta 972 zcmZ8gX=qee6n^Kt_vVep(KOD>Oq@*SIZJF(vy2!EY9wT0g<=E=E>X!MCTX37s7(Xn zpFz++l`#6DCZG}&T&l$C9SDU=TuQCtj$jqRh+q&D6O)#rLi&~n();5s=X~dUXS;`n z_{|metV+}wYvZNm)socz=7MLnOz+SrH+!oTmt-!PCMPFvNs1~-MJ(>f-9(yLu5O~R zF+i5?Ur7l_=J}CjmTmNeq!gHz;YGOSiqKn(xQe5N9+wfa{27XD?rfVroHCsVS_&^J zvML*F=rcpl|p~eD~bX+W>#Wp{`c%I{>P4Q zHu@`cTrKG01SP8NwPU#Wf@bYKCLKb+)f9F z5nrr+KhsxE(mNs6PecQ)1CY+q>#S-tIlNL7_+vBT_OL`~sn zkY0(~iRxMW;Dh+0u3n)+B-f4c<6b=fE*ItXRmiN5^8G%yaXYQS!^TQJHX&v=B^1iTx-A(BVn)j<+KY`XV>F1`)^ANoZ|m1I9rs!{Xh*F% zlFlIBR!0f!X$u#`oFV=!Cnr=haY`s+sBM@jjsC4Yl#R0FHCl=BWRyO@-wul6T>C;g zkLT^tnj7}Kj(!?-XW%V13fY`nOQiYP=F=*8wk^r~oY^eiG)AIhfNqL+9d~7Uw{F0w z4B*-Ji<~)(dtE;Kyklg#IBfgODv^*%`5}Hd!xwLfhOUNu>JXRGof;iMN$*Q{%S-z& Oy+>_dSXB1ayZ!?$8XfKc delta 717 zcmXw1TS(J!6#kvF%YRuD)6H$pU7Y`E+R_7dA1WxVr%Jng zVdg`nLN%yp{0vHq=tlUETN#AaL(%0Sii+^5i*2(W&UeoD9nSgA!})TLd+xHarfQ9Q zT}hFDsSxLTBF5Dk`i#w*U}~9$rlzK1LaP&^faG&cJCRYY($x{Wo+7Mwa!xHq^|_Re zm}&+qX7>7FQ{3!Yh;n5Ct1r-Hv7<{-w5n9x$(HjbhLvONEX0_>A?po#9qG|*{7QOR zm-@rNmfu)yDPz;^_^5|Xy@*({Im3^rrI53l@YABQX%2F%mkgc{P9cOh6ESPM!4pc5 zd==Nzvk*2K(Pqn4G80Wc5*VvkSk$t5|QIGQ6 zODFry@`~yrE`ErvAUACh$PZ< ztrJwGk!ekDnEv4Vj&4f9!R7~4jb$w=O`@p<Hq$QP%|2|>{p>O9kipdqNQ4Mv&wmZr)*wSk+Zi9tkVMg2+4 zJcR~*N8e;0uM|Ov78ypc3ZhR+2r45`u>51R?(VHWy+2;gInTp$&UxPVP!E3YMN?TI zsH`h4EbCjD+B*JYni!~NtOcK;1LOdRSCNP5_Ohvv=oC^@(J#0Oo(nUFv16{MJ zp3JmbN`qmlk-V61q83R(#Y|n&EcA4@TeI6`nR(h|MFn!nG$pD|| zW6Cgm;xW^xQOtZRQB8)2c3CAF$w)=Zv39FVu{6nwGcivEicA!QkDEIExBwk3`E=y`?Ry z5vMit)~tMhIQi@g`HyTbqIZlG$03}tgl;<4p?sH;oXuDqp@YtISkzBN%1@jgckf1!D9yVtJmFsPXufy7$E zP_iQFi(X<(&2|3=NePxzFH_&3+n~_|HGIz)EA9Qq5SR{VH>&V9)I#{n^waCM$a~MgPrzoERzpp z3+gZXMd_PzXl!E)?oj`x6)H9{R00!dd8kr8VNaNGnXZI_5TM^7m+aC1C*_7Sfbluu zL4-E4)wV)9U8;QuD=8Xr!#Ev|5V*+`oe9^eGU|o_+8wodotj*&Pbf%NWfMlHYZaR= zs#~c#mxOdL+67N|q%J1HUYges!|o31YqC<_)-T9AD5Y@#Z$0I&8mn@kj?Zq}BExaI O(D4~Vb-cB6kMswqSp4Sz delta 764 zcmYk2TS!z<6o&U&=ggT&9WTx0sG~D_9H*STOk^2~U@4M4NFYRtlu$WcwCv`CMRa*m z%8)JjGq zlNDB?)!&5b6+&dzV!4(@bJ3^;IXi?TYbF(8P+QFY34G8D_DdvK4ffu|BI^=?He)I>NwynR$*ndc9Qn-iVmAz;b#^9WtPgqx@rp@ z!r^siX$XOa9aM$xhEnc(ASW8iRkFjgagdTQwy~Pp;A@Z zyl8DMq>H%J?59zTHK+JfqV5~=L~f! diff --git a/_content/_hak/rune_prc8_top/nw_s1_howlsonic.ncs b/_content/_hak/rune_prc8_top/nw_s1_howlsonic.ncs index 5ea39cc07de11c569cbee04f9ff93ed9f575674f..ae721fd7ba5c9270ee66e6c13a9863dcdcf53311 100644 GIT binary patch delta 890 zcmZ9KYe-Z<7>3XHIcL{P8&-STwRPR&?rLdjZdcF(bITw?qR2=xq7-k08W@H_){iI( zvtG-JQY?z3=r&Rmbd$=8K#G2J`%ws)Wt50T1ljCvP&7Zz%z5S=-ud2dKHcD+n`|o# zt~84RS=srT*4}g3wuaR}1k3Zd#(vx)6!T(DPnh|qUER|`?#6tJJty& zn-|a^lGr;JM}&{Qpu=n6sW3Rzh7ZDI(~eku4ySZuq3#taEml+EWIBXB`abqpal+!3 zvn>{#`jH#=h5F_6aU;>}dd|B*vfYMuBb{9Zcx?FD&~Z6$ z5~3!zM*Dg~s+19QOx_ahTNL`4M%W%d1r2dFEOPj)BOx~RQkd;_+-KvQvPnaOvzDhf zA>gvhU(Sg{V{*cjO6K%$*p|4TXD&vk(}ij7T((|EnY)Pt)fjM};_L_5mo&;V`cxzi z9P^BF)?FMh+c_&4f#frsxfL&y_j1P2flfm$UN>&#SyJDI)2*g( zx`T11#q_8vYG;KAP2mP82|8DpxJ~x;+7i`^Zau8l)bfoGBJa*$VPqrxWwpH}(lz>{ z%&+}Vsp}f$M7$#!nc)BGra|N^ESE9)ugCMVpQ`vt^2hQ5L8-`FHOw!Mga`_X%K7Ov zMGV|4O5+!A{?MRYyt+`QX0)z7Nxhg?Qb()MRkDCPJ@P|IP*4IgHguC4?=}?DE@W>U zrfcw)E>&bx={$C$5B33sUaE?CM|QV_*u-lUYsi~Og)$hB1n6cn@8CZ>MNQl z9{Yn1xGK-nGCZvG(@%V^1o^R}YBpWO)ha(d!t1JpbWhl$DpSjdS6-pGb@x2iME2V!Mg=oH0eY6>~AyRM6`Jdv|DuS=v&@?dj~ Rm5zfCzvqex^5fwhmfv!T{5=2w delta 722 zcmYjPTS(J!6#kvF|1P>_EBD`=a~J1-mTT%ph7YD?i^4#`EOkNlpe}WaT40bN>7#~2 z&x5`sH8YE#ke`8cp{NLYQB&zESfE`9O(_dPvu*Xz!+H3=56<_U%cJ}F8XqF8Q4Q{nYm~vp#>Oiq$QermK882Iv~FTyZpOw?hO($W&jbr&%=(<# z1Pdnn=u(4DTVRiBpZ;pnR&u9^v{_WqoS;F6o0bF{TC+(j--XgkIw-db5?9)yv}KHf z@(XBjaCzJ4e47{_LC8?O`;-wBIO#~N__;UPKU9H zf`%uk8~^96qqc-5bms8ogj4`K1x8lBo)?z)$f1Mg zj)&kbbyPG%0e!E?#ZVpJdBQ7zhR#%u!WME>6~S3*ty+N*YN$R01{$l*27#8Vk16}i z;XSZHD)yH^E)DtZV5M=t9%OvM{|@0dJ+1AASUOtw7!HszAi*l-1qduO7080?q^y@< zf?V}R$rx=^mCjt+a6=(n+9(qrITvH$CyN1UtiK79+|n>13s1MyG=aC(y)6cM(fkod j)%4)hGvs@D=jn^lLiq|N&8|!%}}GIErPUQJp}F6 zhcwY<3+?+6q?cA&tU&6i%!d}Jq^L~lAs_6{OcZn;&iT*xfB$#>|KohV$@lKCy)0C2 z%ns&{D3+wAd)MqkEc6|PmZfP$vLczwLcP7cPbJwZNjWS|S!#(?kzrj(Y+Od2lEv;( zSdy29OGG>^PrAVc#Jghq8Zg{vGUh zALaaP6nIW^ULD#!J306BzT=wM=yeeXb8y7BfrB3q^Ez>>SnNK_rXD$ZMts)UMn^sc0zCkr};6)6g3A zQV+gFL0)XF97$KuQt72ubW}P5u7u>uJSjKTl#ZL5nm`;$;lGadVK}NT(_`_ds?9=M w5vytA<^!l%tD}C+2NnmgVr?s5e=0_}pr`Q(dHBl%%o(DzhclLr_7X9{(w&Ma zcagcMRMKh@$!Aa$kr+gqMw778g+W<_WV4Wm&-ZlKBlK|`YfNV>cQmv?&T$p0`?s zkmRcHqb7UGfMv?qVSsuPKq{J?D|2qU6i$0H7W!JW94=KJJ7l4 zmMIOb^)zdeP`{0Sn8r1ZPhoRTBaN9`G!9=Fq)4>SY^6erOKl9}VFT}!M8K?%H0r#Ft`WYT{5^pm zEM17fx#Kdwj+Y)N6QGZJt3&uctf(Wmw}jq!Bz!H>FHa%9+Y{+?dk2)zNlQmZ;4yX8 zoPly$t0};n^*pDxL;x+do}7d@%BXWg8@1PkV1fd^3NX|a9oLoZJE!Dgyzn1(X4HcGHYMU4bDnrqB~JERFn5TuNNMY2SQ zPGqq3Sea0eBQ{deg)hdK{8gKPngjP>hKo%>g*@Ks*&sfMB4{|zeK*9 i_gpv<41VuCd7F*xqH+E2I$rP|yslNOw<|}@ ztT$r7wV3Cf!=%ebcaTzL;rXXTSyeI7P4qjn(N~#<_cs-?=}=-jwCNJ-tCTB>(6_mX zE%m~-rGPjqjNup8iUTiLne(sVD|aW`Z1_Gk2MyJ$S=)zD^}B4{h{CPCTyzqnTmRs~ z&&6O(D)G|e7=P&~FV$jqZHOPOMYiW7E*L)$Fo-WbdBoNXOnBdB>nMKoSl6bmeMZsztU3Xe6!V%a%j^38MM-ipkeFEs zzrhKU?j!9@|3RwN60J%SCoyo>Ir+>+G^Z>Q7whIRpT2^t+y3Mem$7E04S&D~Z20xR?)TCZjf4`Fhb0iz2If*Q1>i_99OSnS?d9D!(kI?iv)6i@A(BM!eRRyPKT zdk4hit{bxM==DT-a~S^4B4&0M68k?y>w;p~U%$(~9SWya)U{kz(KNh!wu{oeKc#c~ zawR*xFn#c}tova)QJ)UYUIV^)R*SawC{^P|)QCO%Yv?Up-CxcV$L>2;h{g`5nmTX+ zK~>NsVM(U}EnRLZMYL-TH_VBQgASSQ!u82nnuG3$lf2k+WR}jOvU|NMmo=E}(Ie7r zriU@o?biBp<4QzO;&YJ$tv<7{Ao&j-XhTw6rB~#T#RcCabMqnij&)EbY&{q0Io#>7 z&|k<3fGh|H9;efo2v{nHDE`NOljIyl%GkZ+9H*na1nJHasW?;F#@|S0!n5c>YNg>1 zG1dl8(XhB398=Lgd>tC&20x-F9zp2%P2S;0XLyLWj|+FCW-)Jg4Skmmv*)C6efyc3 Q1|X08!pkzm%t))|A0b0v2mk;8 delta 914 zcmXw%ZA_C_6vyv5&(oF(EYPlLZKZFt6Htq6GkjQJ8_!tO5R54>1$1@}2oNXk#W`V% z@sryGV*Uz?NH<b_m zlXK2gTv-@Xva7X-udXD#u|kOJ`x4n%L>r}x-X}VNlU%a4OEn99DYU%2oD`}_h(Fk? z1F{|HnK9CjaKskF8NL2*be+5H%b~rUV2X3KQ^Rc%S+qF#`ID%tZKn*ln zdg`{?zJwY*FS z+9|fa_n+b^d|lVU`F>gQYKYkO9g;g=ko`M~I3v)X76=>u>ab(J{v9s7gWUaY>~DCU z%|BwI;RqLIA>25^1ueMVc%RpH$mv~K#81DDo4aEix{5=ujd92Y-=0r6*nMKiF0bzK z5*JM4muM#!9z)sQYQ7xCH^mM)y4S{hDS^qRM?7&3?t?{0HBa&dzdYUIVUCYs>3EU+ zwbepAJ}Vd6igi{cJ^O}7k=mS%8{J9z7SY~1{L>SnpLJf& z^A;KH9W~KWL}%=9pX#PQ#7`ZdAl!Xr+^)&`K24>&h@Jj|{zPP;+EVKl`XD{V{J7IB6v|3DOjq6-zO{^%E98 z@6nq|T@GMqf?mOk!$G=_reTl=4~L(lIKm^r@;OS+@I9ffq_A?PWwhE*zOg~9&(W`{ zzH`(GxWAoPt~prouEol}$^yANTf8_%4*%x#PCPdKo2_V-n-i^gx68w7^VJ7FG= tb8Eg#jPF{*Ra3~h_9;h~Yn`s1_Dv-k7b^L(E7 z^ZDr(pS#0RUe69o$+K%S-I8?STvyZysvPazL)vXbfk0pmGh~b=RWQCH$!bZ03KQ{q zL|5rV0iSZPrK4-sfQKp3Z0o^@&Ips!$o4U0IW254pv1YJ?d5paX=8g1MwQFljcd;1 zY}R5v)g+ozHEQ}5Z5e+=CLNbmk|c4Q1*g^<1lC^H@#P_JGb>9fa&BaVPsN?eD=j`4d!Rufcd` z6gqQkI?KtBxJwZea4lz&^*dL(9pyQM*ZvLz_3S zb{wOdo7tI#=kxnHCWk^=A>FnABh_k&a>Gr-3|=ep@^`7hdBx=-1M^fhe%bnn zFD=5qZBCmIG)}T%xY{8WitiIoE}(aRx_I&>E$ODeO_BnEZ5k z|5Pk#cyK=W|3q|Ek*w8(rqW@t(1_YMuG8D0uerPCb@~vKHJf<$ixt5m$_{wd)PbQx zeX2f-gr{nasH`iaOnh0lk&i}-zw4G&+~0$xcO2Y*5Y~D#-A6(Fd3uPy>b=^E1ZAYa zGFloEvA1D{F2dDVOeXjmT@;DW8_TpGnSz!KA)#ppd2pb~mXs`Og3@EF2s^Coi#)Oz zYWkVE^Lw;6cTf!CTfU(e;cs!!64(cF-~(S4eTvII2TkFwFFxHIYOpUudL|}VRXH&s zCI0a?%7P;p8Lh)KEpD{VsAvz)w9W7lKkjzN;r%1Oaho4aN2j=LmDt)|p2+(I22S_z Y{svJnFruaj+&goVw_C-JXInJ?0{=W(_W%F@ delta 906 zcmXw%YfRH;6vp3k`tJpurnFR4pf~ou7TFhB#w7xs z8Hh%Fmf^BwW8OBFS;W@_ryta0CiBvvS@uD3&I_3=ECN$EqYn0Oi67p)C%-4>InPPX z@i87Ate#5Fpl@ASK zYomik(baf@{z7q6kgBk^X_5xe)?7}>h&KC3fxcy(u_rCI`$24JsiY#DYH`nZ#1B%; zoaIcd7gVB?Pg;Iw-si$_>mHhgp-stXZTp^9!P~CVBUHA7?6}vyjQWroR%r;u;RWh~ zxc#Oa@uFE9qiD|@C)Vf2B7qPNN9c-t`Qss-uFmE}v^oXOj^o_vMPgSn#yalJ>hQ+8 xXm*akxt{rW*xARQY?NQ^s!ips3Z}l<&!Nk5|F_5V6h$EN2UmP3bNgEj{{xe1IyV3S diff --git a/_content/_hak/rune_prc8_top/nw_s1_mephsteam.ncs b/_content/_hak/rune_prc8_top/nw_s1_mephsteam.ncs index ba8664b5019c7c6825d3275132407da959d57952..f61d7ef1c3ffe593e2f5c74cb35b74fcdf60220a 100644 GIT binary patch delta 1057 zcmZ9Ke@xVM7{|ZQbKm;`1%Y=bLpbgy98f3~?f_BY4oO+vG95!4Ysi`*hRjJn__4v^ zhN2Ppj^Yn%2%A7t0zZn;TE${BP{-D0w(=G&W};N$v^k-!@7>Yn`s1_Dv-k7b^L(E7 z^ZDr(pS#0RUe69o$+K%S-I8?STvyZysvPazL)vXbfk0pmGh~b=RWQCH$!bZ03KQ{q zL|5rV0iSZPrK4-sfQKp3Z0o^@&Ips!$o4U0IW254pv1YJ?d5paX=8g1MwQFljcd;1 zY}R5v)g+ozHEQ}5Z5e+=CLNbmk|c4Q1*g^<1lC^H@#P_JGb>9fa&BaVPsN?eD=j`4d!Rufcd` z6gqQkI?KtBxJwZea4lz&^*dL(9pyQM*ZvLz_3S zb{wOdo7tI#=kxnHCWk^=A>FnABh_k&a>Gr-3|=ep@^`7hdBx=-1M^fhe%bnn zFD=5qZBCmIG)}T%xY{8WitiIoE}(aRx_I&>E$ODeO_BnEZ5k z|5Pk#cyK=W|3q|Ek*w8(rqW@t(1_YMuG8D0uerPCb@~vKHJf<$ixt5m$_{wd)PbQx zeX2f-gr{nasH`iaOnh0lk&i}-zw4G&+~0$xcO2Y*5Y~D#-A6(Fd3uPy>b=^E1ZAYa zGFloEvA1D{F2dDVOeXjmT@;DW8_TpGnSz!KA)#ppd2pb~mXs`Og3@EF2s^Coi#)Oz zYWkVE^Lw;6cTf!CTfU(e;cs!!64(cF-~(S4eTvII2TkFwFFxHIYOpUudL|}VRXH&s zCI0a?%7P;p8Lh)KEpD{VsAvz)w9W7lKkjzN;r%1Oaho4aN2j=LmDt)|p2+(I22S_z Y{svJnFruaj+&goVw_C-JXInJ?0{=W(_W%F@ delta 906 zcmXw%YfRH;6vp3k`tJpurnFR4pf~ou7TFhB#w7xs z8Hh%Fmf^BwW8OBFS;W@_ryta0CiBvvS@uD3&I_3=ECN$EqYn0Oi67p)C%-4>InPPX z@i87Ate#5Fpl@ASK zYomik(baf@{z7q6kgBk^X_5xe)?7}>h&KC3fxcy(u_rCI`$24JsiY#DYH`nZ#1B%; zoaIcd7gVB?Pg;Iw-si$_>mHhgp-stXZTp^9!P~CVBUHA7?6}vyjQWroR%r;u;RWh~ zxc#Oa@uFE9qiD|@C)Vf2B7qPNN9c-t`Qss-uFmE}v^oXOj^o_vMPgSn#yalJ>hQ+8 xXm*akxt{rW*xARQY?NQ^s!ips3Z}l<&!Nk5|F_5V6h$EN2UmP3bNgEj{{xe1IyV3S diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulschrdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulschrdr.ncs index 1f7a0d2210c6285600738dfc99b1be13e126af01..acc6c119133987792d665c4e14afae8f54d48c5b 100644 GIT binary patch delta 888 zcmZ8fYe-Z<6rOW-?(X_nGxX}}y6(EGR+i?LB^c#nO`$?7(dc1;kMdFJN=a5w6a;~Y zad1s2NrOrPdyF6zL7F8=Al8pw5`s}gf|?akg57&pLHaRsX1@7mzVn?q&mN-pF`Dw+ z1@U1jdgYjs_m}5QFaVJcYmnUGni83WS z+C%9RUx#r$WRV;g*G%b3u(eRB)QC2Nn3kLpjFTKNX&mP|!Xm~*B*0boUratR+Ony!WqkrP+VNq-g1Wf9qYI{2-#ZW-Z z4NPpHzQ`yVj;X;_6PG$iuV^Ax(ikjG)ALS)u0J~4Y2F&HYHnj zi*q$-)gGh19jOO$67|QBoAB-EJmMC%U3n5T(b0XK zFi1;_Jy1yJi#KA&R?%PV<`6;pk{+nrE&`YzWTNEU>EEW9eOxYg8RjX>Z zi^j?dz)dOTj_5e9SD{6|eCaCY!Og|d@-c*F%CBgGVES0`6t+=kWi0%q!AgQy+FO+Z zcc{54Hv5Iv!&3`A?CWGz4Q!fbRU%sztO_Y3SVJNfD=P)`L;7CT4MU=%dRPbN$Wt?n zJ*_m-*hU{}%l$>G_F6z9qopU!RO0Ev`!9v5ZhsUU5r>;<3~+@;TRs~rHflfnhi1JF JG2z{<{|D1N0`>p^ delta 728 zcmYk2T}YE*6vy9l_U=RH#N6I(x-a)`nl7i46eAI}l~9p^lBVPgHWzb(Um;mR)@6vL z2UMb>6f{Ewdko6*LNKT<((*z|p(3k`3N;c!Dtk8xx_Hic&i{GN|93cj)A(cxHD%t4 zl**he&pwX3abQB@MWK<+2>jU!3yU^-l>vD%26eOruRSI4vS)hS#X$5AFTU4RpH*MZwEDt`bj0ik(hc z6_V-MZVg!t4y8V#r7cLBiTs9H)V+`}8B}g;MpprwHKqZG>`PJzRT&e}nY45=PoNg_ zY0TlsE?KYj$O$b4s92^VODB5LXvQ*x*%v8j-N3BNtkxC>=sr*3l$)5jOltcuX6DjN zvYF=WO}J~M=eEF(i8jFO6>4{!L(d?acdl7jx_2oa7yJ@K%Z%lvJakiC{hbaEVy$AUvWhpo8+4>q~zeU;&H$^)@CslcMGUH28nTP_C3+c*yM?0e&!Ox7)=&{%1j@(m~P9NYVkF5JgZy5Ok0dMExj>3ZjB(X4WA3@x9-BJm34g@56WeCV#ulDdn*W zby?xK33CLY=iB9!_Ec6cpd&SlwJB&{8}OHEE;3E?y&z@@f)F0Wv(zZ(q;&dJRs+y{ zlUeUYrB4&}4oU6wWz#{#eS27ujPd@Eo8F>A9!z>Gx~#{XuF7anPcy1VKKJsd7q}ge zu~5-j?zTDvFUCVGr>icy+>iuG=M$=}WT z@cnVwK!HM>SBl=T+c+xWVnHU>mU+Vcg6I~6W^!cskgR%C#TrsLEsFN#2;7gSOD5Ji zfO!_S*X_81<-QU{vFO%cm>?z!j%BOd#*Rxv$T_Y5A>D3)rX`pY7^869=S=!wT$^7j z@r#%C-bP_XiRkq>jl9WFM+AW7&uOcp$&M9G(xF5N#Ss}?8RGsML#3Fpy|Ua*`%u&+ zV_@Z8x{r0$>uD(ls%P@CkA_WYvYWc+lyRl&ngs=5iE>IlF&Oe6C3usH^Lx zOk~uT(=IgCf1y59HI!MIlMPcT39lQfq@$U(WM?p|v4UdgY}B%J(PO8a-U*x7*3yn* zqVcG)pQ#n+SMMV~W;EZW1)VKn`USlO6vow-NpumPTEcT5SpUH^tx_6vyv5o7<9{+P=`~?)C2O!#QOaUdvbl&XisVWK8es*|OBw|}6k!kbP!DA!NEx<`9?pUPpTqBb{hHy#Q_iZdZjkn_ zFAMAtgwe!ARXbO{|qf`rb zOBLVPpoUvkG(2_=2g3@Fg>WG(QWnO;la!0*)_OXDd##H!fzilLvf*o_g0zsMLH(7& zFua%j(E*lkOjZ+s^I) diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulscondr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulscondr.ncs index 4f364f0480533a54c2f125c287a69b04be0543e4..39b62d7b842f9e0bb36119deccd15e400ae56836 100644 GIT binary patch delta 888 zcmZ8fYe-Z<6rOW-?(X_nGxX}}y6(EGR+i?LB^c#nO`$?7(dc1;kMdFJN=a5w6a;~Y zad1s2NrOrPdyF6zL7F8=Al8pw5`s}gf|?akg57&pLHaRsX1@7mzVn?q&mN-pF`Dw+ z1@U1jdgYjs_m}5QFaVJcYmnUGni83WS z+C%9RUx#r$WRV;g*G%b3u(eRB)QC2Nn3kLpjFTKNX&mP|!Xm~*B*0boUratR+Ony!WqkrP+VNq-g1Wf9qYI{2-#ZW-Z z4NPpHzQ`yVj;X;_6PG$iuV^Ax(ikjG)ALS)u0J~4Y2F&HYHnj zi*q$-)gGh19jOO$67|QBoAB-EJmMC%U3n5T(b0XK zFi1;_Jy1yJi#KA&R?%PV<`6;pk{+nrE&`YzWTNEU>EEW9eOxYg8RjX>Z zi^j?dz)dOTj_5e9SD{6|eCaCY!Og|d@-c*F%CBgGVES0`6t+=kWi0%q!AgQy+FO+Z zcc{54Hv5Iv!&3`A?CWGz4Q!fbRU%sztO_Y3SVJNfD=P)`L;7CT4MU=%dRPbN$Wt?n zJ*_m-*hU{}%l$>G_F6z9qopU!RO0Ev`!9v5ZhsUU5r>;<3~+@;TRs~rHflfnhi1JF JG2z{<{|D1N0`>p^ delta 728 zcmYk2T}YE*6vy9l_U=RH#N6I(x-a)`nl7i46eAI}l~9p^lBVPgHWzb(Um;mR)@6vL z2UMb>6f{Ewdko6*LNKT<((*z|p(3k`3N;c!Dtk8xx_Hic&i{GN|93cj)A(cxHD%t4 zl**he&pwX3abQB@MWK<+2>jU!3yU^-l>vD%26eOruRSI4vS)hS#X$5AFTU4RpH*MZwEDt`bj0ik(hc z6_V-MZVg!t4y8V#r7cLBiTs9H)V+`}8B}g;MpprwHKqZG>`PJzRT&e}nY45=PoNg_ zY0TlsE?KYj$O$b4s92^VODB5LXvQ*x*%v8j-N3BNtkxC>=sr*3l$)5jOltcuX6DjN zvYF=WO}J~M=eEF(i8jFO6>4{!L(d?acdl7jx_2oa7yJ@K%Z%lvJakiC{hbaEVy$AUvWhpo8+4>q~zeU;&H$^)@CslcMGUH28nTP_C3+c*yM?0e&!Ox7)=&{%WjqHZ$9I|i34Ibg?eyZ_$kzaetpY63ZWdPY#Y+8KG~^Q75)KNlLqY{ z;%?k1NmJ$RB2g75~&L6 z)OLH|wh?V;1eva#_P|2pXBtO~nu!6212 z>pK-Zk-xJha>~DjiP|a3&%tHw9Odo8s+>cV`&GWrnZ+qG9qzzxihM?!-xv0o1@C7< zVXTgunqQK&M5)gUp~ubQ6zp4BxYr~s(YZZ}X)@h>Cxj@z`Ip8-{J>mEKi)~E$rH@d zhX0s~!uZ^{a-3tz>5szf@PkB$2aFCg*%^50<6oq&q%?(|kIIcJ)(AcY*H#VFo8yRV zcA+U&3=s>`vtEQ_3+VMb1C>p&W`#E+rTh%PkM8nDz8b;x^Xc?OS-Czccm~=x_HZA{ zE0*(vxKuIBd(l?8R7VCXi+B>WsL=cUp)Jik6??1i@MVbB1od}t;*^mhv4vtX$4Y*Vu~kL9Ks zLi(3#A{qZG>vf$#Ky3$qBund_SSpkv-1t@hIZ3vfK9lnpp6*G9 prSSrFKb9Sh8@zn0bTrr7_(|+J`pI5>(j2p2XglVYM~Ls}U~C81%~Ll+~Z=%Sq&>tV08_Xq3$|IOaF`*`9mTZ;=z zd}Ub~!2(G-Kha|yHw3)Th-PPJe@JqIr1#%!V1pq{rX<-TNs_+;HaUsnkSnKZ$w8CP zt3$~r8}!PN(HH+lrxJDK&BakUhqYn!$pO{`M)l^o1KD_jJ*jY-62$w(&LivD6Y z&B-{h!lsG`mSHnH{-A$>it9=;D}A~~!n}`jIh3>brMTG^M7)~Ht`K?@J2tBGSy_zs zd>am^r_F(P;l#;yxa@n(fvw@gA`Pch7d}|6=(i_D*~2VlNM{L<9S>O<_-Ci!n`0-Z z=3&yMh`r7@qJTINA7b;8o=8R;C`Uz{dpBiaQ?7zcrx~5@JT{GB)ZNU%6x1Yka^@jK zd8Rqzn3(az5T`dG#&?0!MsUSOam)FN-mMHgV=S97+2**dZFU$wcwE;PreS zsuhWOvRD%CH%5TTr0n5q{Aq!>Y#i0Ue!RW z5vpF!9bZIOb)igFe5~oEg=nuWrhS;K{YpJ}yLk;ckY1NdGV1CoquRnRCUxRbT?s9R zYfHfCja*iWRGQlxB`P$C$}OWzGsp|IP&C#TIWQKwLMzd-O`~auZ6FU;)n`*Tn(8$g zM0MpXrVk5-%Ussw(7_YxdPX)FQ8VoI}59{(0(7f{o-?X4%mji1X z-beRZ!c$~Pm*79>L}_C;i+nNLSnZ}UQGIep6m?Jk6| diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsdexdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsdexdr.ncs index c042b113ac22947bab282d2a6e96067e76413fc7..60b1f47c1f34227b47f11e115c7dd843217c44b5 100644 GIT binary patch delta 888 zcmZ8fYe-Z<6rOW-?(X_nGxX}}y6(EGR+i?LB^c#nO`$?7(dc1;kMdFJN=a5w6a;~Y zad1s2NrOrPdyF6zL7F8=Al8pw5`s}gf|?akg57&pLHaRsX1@7mzVn?q&mN-pF`Dw+ z1@U1jdgYjs_m}5QFaVJcYmnUGni83WS z+C%9RUx#r$WRV;g*G%b3u(eRB)QC2Nn3kLpjFTKNX&mP|!Xm~*B*0boUratR+Ony!WqkrP+VNq-g1Wf9qYI{2-#ZW-Z z4NPpHzQ`yVj;X;_6PG$iuV^Ax(ikjG)ALS)u0J~4Y2F&HYHnj zi*q$-)gGh19jOO$67|QBoAB-EJmMC%U3n5T(b0XK zFi1;_Jy1yJi#KA&R?%PV<`6;pk{+nrE&`YzWTNEU>EEW9eOxYg8RjX>Z zi^j?dz)dOTj_5e9SD{6|eCaCY!Og|d@-c*F%CBgGVES0`6t+=kWi0%q!AgQy+FO+Z zcc{54Hv5Iv!&3`A?CWGz4Q!fbRU%sztO_Y3SVJNfD=P)`L;7CT4MU=%dRPbN$Wt?n zJ*_m-*hU{}%l$>G_F6z9qopU!RO0Ev`!9v5ZhsUU5r>;<3~+@;TRs~rHflfnhi1JF JG2z{<{|D1N0`>p^ delta 728 zcmYk2T}YE*6vy9l_U=RH#N6I(x-a)`nl7i46eAI}l~9p^lBVPgHWzb(Um;mR)@6vL z2UMb>6f{Ewdko6*LNKT<((*z|p(3k`3N;c!Dtk8xx_Hic&i{GN|93cj)A(cxHD%t4 zl**he&pwX3abQB@MWK<+2>jU!3yU^-l>vD%26eOruRSI4vS)hS#X$5AFTU4RpH*MZwEDt`bj0ik(hc z6_V-MZVg!t4y8V#r7cLBiTs9H)V+`}8B}g;MpprwHKqZG>`PJzRT&e}nY45=PoNg_ zY0TlsE?KYj$O$b4s92^VODB5LXvQ*x*%v8j-N3BNtkxC>=sr*3l$)5jOltcuX6DjN zvYF=WO}J~M=eEF(i8jFO6>4{!L(d?acdl7jx_2oa7yJ@K%Z%lvJakiC{hbaEVy$AUvWhpo8+4>q~zeU;&H$^)@CslcMGUH28nTP_C3+c*yM?0e&!Ox7)=&{%vY2_rD9 z_|uDJf(6-&y>X4S`d~$b5fpuiG{O*~{9?AE4;pqiqo6P6{Gb2xJO6Xe^L~tPKIQm| zl1h17PWGx|K^VAsH@-l}@-39<(%9<7DsLhV>vS}WpvQnqIs-f6F{+bUPQ;`xkL| zS7m+1h8t?O3uWm+&g@VgraOohQSDfxSnLm(-3_3;Bz(^(WN8#$X9v5wG2*<;&QI9k z`oS5q=y~HRp*zIHYD=PK`N7ovEe2yg3c zJYdAHdNZZK)G$mYylJSQqp&p2(S0QN)~n7=Up_6v8DF(@(G(uUHT?8dQVBNrt&&~T zhAjmANhB&={wby)8k>4ZkJ^f4#5E65IYygpG>;F>ARAg+a_Ke(TWmCrmn|u7b0l)N zN66BvIpIO;BTmXv@ZagiFk%9?=!G&8coju;IM(`_4q(^*WCZtpR#nyhaegqQ*xGic X(njUTsXY?)A$0B&H#n5Z-cIdrC{gXb delta 716 zcmXv~Ur3X26#eeq+&4|x%uUNb+vn%!+?LY?nxSdYLNG!ME!@&ji|HWeAXLhilBkCw zvh&a%BTJz_LV^5xFf7O zhqo7b_e#>8rm>WA6WiaR#$@AMKSHKF+Kh#W2f3a*YXUntbsmhk?IH+WEnipM) zn=?ZgR5Z3YF{#`)s*5&BlJe6L$?Y+!?J-cGp)M;E9@z-9>Xg%Coa23*^Q*7e@;0jN zLQP(T-4$XY&q1Uj;Mgs4^Cy^G7-+P>bIgd}1r=wg-V)$)}^WwY4QlAA3hh3fh8C zC>0s)wR9T6_GKDFW5<5Vz(_|K=`q(4kgw&$?nbaHR8N(-5>mI=pkW}TjOrWJyM}L z3iZ)4$C7Ps=?XywA3h zf>L?yj9_lQAeF9fb&QosO&w14XtqS0xfB<34syVXV2(T6Z5SC5M6)2&l3~NpMA;%sT6@ApQPjg9 z3er}mVESrTMX^TiF&kt-yS@@dadGcoUNjN4R7>w=<4V7o`St%X&1Qk-N2Q}2RSAA_ z(~#P|e2bO)2ei=>RHd{?Bn#@+eWCq2fM9t6Zj>q1f`PI{9Jyn#g5^zSI)UDeXXr7u zR)lB`UQ{gP_V21=)0l}n@-U^+#~m)rtCT1iWtBbThOMfEj$lL8XS#`!>SY=duAWO1 z&{w_Q+UC{`=)}yLQYyfa8c(VsT6E~28KbRhMo@vM_SQt0>d~?71lf^W`;b;(wAM#I zkz5Dz;Y!^sx{5b-zQU)*PJjBd)6vb=)krclx8(@gc3nt2b^}TNS6MSNmQlF9o1Up7 z+n<|ETbwdzi3^dCO8d~adn&qi4#w^?Do)H1aHt^(J-a&j_GPtV_r__oQoY-_!%C+R VY<(Y7^}+b>A1DcX)J5TP%P;dc2qXXi delta 770 zcmXw%TS!z<6o$`QXJ)+Nb#A7O=gjepGg_8HQBZ1S83l@@U7Uwd@K&ZKVs@b`Q4dLx zKcY!$T9{fyvU>;&!U%c@vgo0QF4%)n6l9qYHZh$U^I`9`*S8nz-=W#JB6#(zOZbcbSom!r+~rnru)N4bO&H(zlcqxe z&HXVrqP9qZ>_5O%%m+?T-O|;jS zQ68SxYspIZlcexWi)*t)g|dh_FwOJ<-x@AcG^R_O=sa|nicrv~(>$shL23BixQ0fN z-K6JFh5a4EnuKm K6IXgGEdKzrjP*VM diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsholy.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsholy.ncs index 91f2d46e5b0a429805684cf623f79eda4c561882..3b93769cec2ea5d3af348da77ed3fa8889d9e6c2 100644 GIT binary patch delta 848 zcmZ8eYe-XJ7(Va2vz?g^XKgN>b7ymB&go2}lFFE)ok7u4?BPCFyA~4xezn2^)3u@(*Yr3V3dgi5sB|>wP zGHQ=^u>rj`hf|L^fm`zK(f6eNlT3T(-<$8F>M^!O%(hqoJz4@mBen@&5bdQ@7p;QP#oa`qo01*vChO%u8Vd6h1>`AWyCNdYa3hfMY(v_(2z3qKyS9IP-WFNE62ja^%oKx}tt~Nh3vd zu%F_JcVKV7SW&zp!@eOZFSTG_8=WgPLKqE|4ugS8oH^WSJtKYX3b~yT^v*c}59vl( zhQuwFCBYi9xeB;mgSVIgYIEg68cn;*oWLr)P-l54u%fPf2BDUIRdj%w&Qv~!EXu61 zz%tcV5m<<=-ULImwb}yXR9GEmH~QR;I+!@MWZ~^x?L)%+p8i&)5(AJfR}aD~F{5Te x21h8o{tYzK)n*-G!@M+oZ5YRqyW)e!!U)I~wVgE_T%l9lb6C)GQb2woAC>nMlu{`u z=rjt`Nzsw{F)a%>(CoOCn8vyiv@xKiUgIigS-a_{o;Ur`Qf`V!!Q=!Ql0+1rs1mXy z-quMLPCZAF*YF%y=H($A4li*Uvq~CV1Xc&8_{eEhP_$EMzk$5g4!yh(8QYA6a*JNB zkAO^tX5F~{O|iA{VX~P*#RX3P);JSiR`*<0U?VjyVT82=&8+K zh}w_z%6=K`rBt3Zh?xVlnzex$57?q(2cYc^t?%wdWs^Fx@1b&tikxkj{_^HP5}R{c z0PPwI=UhVjGgj?dOJgGyzqL5)rshloeG>I-{BVf|nn+*s1Li5zXru|x9;)=%AWW^E z68xe>NH*bVkAbUntoAO9Qn}dMcGAgZ3o6v8SBNr(e?Htcpyj|YOtYRdQ@ol~+xMM#=I2J!F_tyy@;n4*wdaX+P^o^?L+_o diff --git a/_content/_hak/rune_prc8_top/nw_s1_pulsintdr.ncs b/_content/_hak/rune_prc8_top/nw_s1_pulsintdr.ncs index 44f629c2f337d7073b921abe63de0f01b412e704..ddf504a36185e60ad019c989dad82595d3d641ae 100644 GIT binary patch delta 906 zcmZ8eX=qee6n^LAzL}TAF@c$v#mSp_v$>=ilTcNmO+4SM5n9H$2l&fJ8+vhf|bchC!Q1@`qEl1UeO3&ae94&Yz@~r1vYU-%>cx7uJygr$A6`?vW9_#&D>ak2^lFhsb}*e_mP|oWX|?;E9Zx%L#eO78ue1Kg1dk6*WnKKqP9(~s@_88(Ws&eN zl+&kFvR3(zGolyiIXY4mJzTpOiLe)!^R)xUe0E>J0%IrFbmFq{3s>JnTi|b=JuJTp zP9mp&tMKg*}6{V9L z&R2z|%M4mWE58Y0UykG%bg!tzFsa`xwV;El163?{L8{X16jBohT$}DdCj0*65+gkUJhONc#c+%EPanx=M2V%lyryS;-&Fx{SLUAEC zZ_F_5!}9h%D#l3rHCl$_9Z`CZ8y%o1)+Fc9DfA_yi*8##pUsXb!mn8Ozd38OQPAN` z&QfL;+eFqWb~5E3Gh52$Gsco9>5lxVb2yKFKq@uNgNKmrDaER;7c38oUfwV%QddMCv zH2n(tQj|uQmtg#iB#R^?2tq3##Pp$}eCa`b@Fi&U-{|3R_Sxr;Zq9B+zwDy}n3VmT6TqVEVYM7T+=6TR@7(ov%dl%hbFc%n8=Hevr6P=`k4<#(3myGr4s18bHY`@iJPc&-QYkj z-neGDWEh?9JuV&*XVg^UqU-4P+~dMcBxOJ1!ZJj%Z*jrn@dt`%@T$ZmKXJu(jRP}c zS=(`m+~7tUuXZ5hwFuj(8kt7X*0jrCrI-V?C5T7O8h=sovH38sAB)$iE#n5diAdWF zEugV|fNJr+y^?1pgyCFJCNpX~=4d~n9d&dSCp&j3f~v0LWXDKXImvk2b=o-P(k~&+ z!WV9!N(_a)q~dPVy?lA;KUsHiaO z-zJq{L1~sPPlzI-g^D5&`&8)%NysFj23sHldv6qyKHPiH{hj~0|8s8l1OD@nEjgZi zwJ0@Z(JD!5-+RZBC|K=5hKOOu5^NDp`igRgf|Ekw1PeNa%4#@Xi*$DNf@FtqwVG8O z$I%&VkdJe$8Kow>_FXo~G>nPhuQaTE2^kM$+XoDVXR^|!S49PlNg_s)BxNVuN;HRC zP^Kht)LI->;#rZ==nlg(<#eF)siAU>qkK+NpmVixnMcS9jYLL>1;Xkyj~i_6Z9iLY zea=d^Zj*=%TLaH;hA%Q)dt{qUG^zDR)G;Uh#PaA9ym$>NY*F|ey^PKG;j%Y#YCW#m zukfN5$d38J3;U4UX2sUnADr?8o7HwsiGxqQ%E`OY7I%ynjA;puG~(1mlsNZr>IjM* zcIVecLgmDDWl7 z{->Kt_;=RRP3#Y8TDhv>Kcy^7lsRQRrP1Cyzu9RNzuh(3tCa(@`0a!~c$juScdej= zQ-wZ$f8GEv71eP6y;Fq>4i+WyyTNJb(d6PBnOaa#a+zM@S4krkqONoWcT{Vgr5-^M z7^~C^e()p$|nyZs~s^eVKUH+>86_2 zzkobKE2th~YDIc&3x#5n$BKd48MfJcFw4xkYq_mHmq;# g;=6CO@y4=f+OFlb>@d?sJU#P?Ym2q(XZM@_0uC?mNdN!< delta 695 zcmXwzT}YF06vp3ke7C7%X1aHqx-aLZ`8~laLKDKz$QY<-9d z)c{*F(4e-6-zv3=)YyprNow&;`zw--f3c+B$NHJjTT;|_Nw!Loq`!(%#mw<;_>>Gz z^rK&KvVITlDdUlj&)XL+0~!ryA{}kpCe4Whv56?u>L43aG=}Xgbs?M`GCpJd%Mck?(wo3@sDX8|5$hnO}nwu%tClPz3G3M=jGDv3N1;%4g-(-wODgA|Qj z?N6y33++x?$DekP6$2glbO*B?&XPHz5Or0Ocj!C1&7p)atvOrDiVaygG_m0%{y#^G zBBXat(kt<3cDXC+*X10)!y#!@R zbcit02a6UmEv^uS4`oGIkUa!}Z4*-DlY!Jj3DcdKBItkk`Mz@w=jWV%c$~ve*j8D# zS>3ucH@{R629Mvh#VM=~!=WJ zSdm2SoTM658eKrP{S2o{C|=~&-`Fi?n#PCtS(?^gCd|pE#3>8fmpBkkvSUVdu(Qvw z@TksCNlg`uvz<+%Nf65GO%x7?zY1cC0Xl3bNei%R9h%d8>@)*u*JbZ`gwp0v>NzBP z*G85Mg5A3WQ=WWjF0!ZJG4_up=dFKlCi*%!t6M+o^Am@iDA64Hg!Y*^P=~weF1*Mn zWa&QS%x=!?!RgE^obd&Df$y9?tNXL!i2a{XkUhZu61>j7##$e`a(Y;c)2+D$#F^zN z$~(pZ39EA50e3V)ND$3}u%9BSgqek^C6@DsC>pyElc1<-#qRZnY}6c;3C8$N6vg6) ze-(llBceAO^dXLp^ha6R_Mg&h7HC!M(U?Xb%lqc08NA=vpbu62vhdVfBk^LmowEL9 z%No&Yi5kiaRXp16f`7*(9f=C8)j_(51J!FdH1-E5)8(C&X6nVZJ%jWNKlij#6`E>R zbAPRVt7hKB10%Ru=i`A>n5;{sB+S;0k{eg+E2Wbz!T8sC@VQ}w!Q}^6 z&;kU5)l$DZQp`IVd6D<)4#f|YK>f0JaOl?@%)JqDUH9e+{=xO%RFWhMc z`LMR7fJSh%#YYnuXh{mBM(y_H2-%9^5h-1XB1tjRSgl5jR>s?wVS28gZk;qyEvh?S t(P4xRC!qA;S7XZRoZtsT`j5_C$yA|Noo|)sGM@H*;k{e*+m}vQeghyJ`_BLX delta 727 zcmYLGZAepL6u#%&xlMC*=jP@|+wQJ+bFQfyEDXeNune*vhLlRtDs7TeDUHe>ef-NZ z&mS2Asg@&>*2jG%mgI1tHo~^8*&icgjS%x?<|CbwGrk)E43= zIZ85JT~{1jroN*?Fiu5*R;ZwGpaA>tv6a9Ej|0ExTfHm|Oh?n=61IpMQQ~6>mc^1= z*-Xx_BY(qVP_k~Qg!8oPa#wsYFK%Y$4 z3Y&c#2W31PYFtEEpes#XkU&YH7f?>`LNct;dWgVEH=6zMfF_&e!WoGEpRz?dr@R}R zHR=PYF}LH{oJUZgO1VOeG5qu4;(U1jdgYjs_m}5QFaVJcYmnUGni83WS z+C%9RUx#r$WRV;g*G%b3u(eRB)QC2Nn3kLpjFTKNX&mP|!Xm~*B*0boUratR+Ony!WqkrP+VNq-g1Wf9qYI{2-#ZW-Z z4NPpHzQ`yVj;X;_6PG$iuV^Ax(ikjG)ALS)u0J~4Y2F&HYHnj zi*q$-)gGh19jOO$67|QBoAB-EJmMC%U3n5T(b0XK zFi1;_Jy1yJi#KA&R?%PV<`6;pk{+nrE&`YzWTNEU>EEW9eOxYg8RjX>Z zi^j?dz)dOTj_5e9SD{6|eCaCY!Og|d@-c*F%CBgGVES0`6t+=kWi0%q!AgQy+FO+Z zcc{54Hv5Iv!&3`A?CWGz4Q!fbRU%sztO_Y3SVJNfD=P)`L;7CT4MU=%dRPbN$Wt?n zJ*_m-*hU{}%l$>G_F6z9qopU!RO0Ev`!9v5ZhsUU5r>;<3~+@;TRs~rHflfnhi1JF JG2z{<{|D1N0`>p^ delta 728 zcmYk2T}YE*6vy9l_U=RH#N6I(x-a)`nl7i46eAI}l~9p^lBVPgHWzb(Um;mR)@6vL z2UMb>6f{Ewdko6*LNKT<((*z|p(3k`3N;c!Dtk8xx_Hic&i{GN|93cj)A(cxHD%t4 zl**he&pwX3abQB@MWK<+2>jU!3yU^-l>vD%26eOruRSI4vS)hS#X$5AFTU4RpH*MZwEDt`bj0ik(hc z6_V-MZVg!t4y8V#r7cLBiTs9H)V+`}8B}g;MpprwHKqZG>`PJzRT&e}nY45=PoNg_ zY0TlsE?KYj$O$b4s92^VODB5LXvQ*x*%v8j-N3BNtkxC>=sr*3l$)5jOltcuX6DjN zvYF=WO}J~M=eEF(i8jFO6>4{!L(d?acdl7jx_2oa7yJ@K%Z%lvJakiC{hbaEVy$AUvWhpo8+4>q~zeU;&H$^)@CslcMGUH28nTP_C3+c*yM?0e&!Ox7)=&{%+qn2|Tr*WJ!>W|Vfuq@F6MNCGJcG7W?%l>`$hjDqQ$F(`W2d$0Yi|KDq`)!WH;9sSC2$`nG?Tbc={QhvjkJDb~M-*vgsk-+7EMD z1vW%Aaq4sYiu%U0yU#UP5fc54Q=WjFS2@LnM(0gVuEBEUBqt4@ZIsn7ijz1k0h8Sa zIc)$Ymjfx8#`u0gG6+HiNuu_#p-GBSacY5ZOOo_MB){LUN#G&{v&o1Cdsx?`?q5S5j z)_#QQUAa!-R!@L*jjzwXqCjP(2je%JShden~mTm#iYD!NWD&|I{e@?k4p z#Me92f?|(EHeBA~BL~)Q&81_wwsn*`acF$^Z~S>x+IT0=qhnX z$4N#V{nOvXx3wXVM^dHj{Y-WEw4;{7;m@|>Wa%ARg#~4?^b;G)K(QDtOQ(B?_r}so ztn}Ivoq-zV$wE@N{;;;f1H|@kC2erQjN{(B)TPcZ?-uDOOqJd2yMV_1R*X~*gs2yE p7fnhQG!%*PT`he7mD*IbDT;Qge0rCOE@MyqXZAL$h3AhLe*=|u@kIat delta 694 zcmXwzOGuP)6ve-Lzi%FD<7>W|@iESf<7jC15YF zVaXvgq)3f43`PD%kx>h2(Wa@S#B`C+Lr734!OXOAq{ThlbGVn^dGw6up0J_3q{2~^ zo#{RxNquwUhFNC&J1nw|6OTbJCs8!IohB5^CQf#vMs~107oGAxcFka1b}BBRa|C1C zQ~Z{%u`LY-O@g?uXf>q8NXRm2MPKMoJzM_bhB1%T=YhBQkR?g6Ns^@YV#R1>qZ{FB z8ppLFPj#`n2Pf1az4Pn#MY0(Yrn7oy^S0UT!g6>V7DEh}FeT~C+gZwVFk3UdV)ab` zk%~og2dCwuKUNiIEYUPGkh6T) zuE6&$^7L8xt&GtPtTcR76?{q_f(o`W*SRo*+)-ER^<9CqTYFWBWg$L;~ql(do{ zUKG!T^K=8EEJiVUq*T#$DRQ8r9`_PWh^+ibp8|9he=3{#y&ayaLY_ScZ<86Dl|^E` zs$4@CQFeSlo^(hUt2RNdtEDp3*A?*Ky0}wUqL2X#CnqQhJ*Ucbo$=Dv*473R8?4YZ z_-Pz74Ts1Arze*b)Ou=lw-N$$X%Lg13MxQEqcg@HEF=Y$c6ND$Mr1Z#uRRR+kOpO0I zQV3zTZIs@M%cuP^b)lx+&yT~9?X{q&<2z4;A=UeuMUnXBt+&#GsOW0f(J-cNe&e<^ JG1BAH{sUQ$-&gDZ*}8sk$j$}KSo8?Fim@^mip2e!A2}KHFCjMiJqQK@Q zrIQ?WIPh6!AJqDS!VG%$Lv!v>pRZcV*+h8P4AQG+9YJ?2f_N-QUAJ*4wvb-FF?ijv zBO+@=qWBzZhofX0TZ0273+QZ-+Ex6-@GZH#PMt|3L`b6G0( zman&*mUYuDcvrrfMKCAs&kAv~4*laR*{)`uMYx)X_YiABw4#yO(Npn=t-yuKAp3!i zO0XbGs%EikI9e54++oc1=Rb3uoNir>`3%jfJR#Sv3u()4AesLvYY&)0_)>M1JyFkB zKQ)>5Nea5=dC;`=Cbgcx-t8`INW4!~PZ%}R@&ttI-6-GILN`0rjO`n}Y_qzhajS)$ XLie#gYsGxym%n4=xL^Hre4Y6hOtJ{x delta 766 zcmXw%TS!z<6o&U+b7s6u6Y9+IjE-~hIA)rc5sCsawdsOEmr2ku$_gFx7G;H$_D~T$ zlqmH-BvC;MrC}G_4pbIB6a^7^dyvc^A?>A5K_Q~DGh;sNz4rRoVtt#v|Be^*_8FOW zdP`iTx!ImgjNRPUXWAmE&SB0M7Z-mr$;Pz!n{=epg_LBd22LC7BF13Qa|0CPOq4;=X|qUiA=hjX zDJ#YzN%;d;jEq971*vi@S)OZyY*F8rsFn`wlat8oL9?7eiM1%Qn(;K$wi@r{yOHWt z=x(X;Xo+u%R1b%eG&kntL=>+y!KI``#D}z1qnc(zKzU7;5C3r&C?gL(n?;OU<2YBv zkAzyH^gj5L+9{V|vPQXTFCfDNitJ~}vkI^5U6gYUHI4kN*# zZc*k0Ov!gBb1j0&mub!2E4O7)raC$0e8s7h7V>n85%;WJWOzTVq|r(Y`^+M7&sK@I z;ZW%ujkAyf=M5vW{Tpzv%uORsts-NpES)}XUapF4|3y7NkKq1JJ^+73BQM6gihSy9 z6Hy1e5;tK-Wj~JvRqf=>@Kw$6K6nG$bqX0US7%07K;;tN1olQ;vujVox{y{~!t>Em zty-Po?_uGcFP-&_dv#)<`U~;DxKZ22qw#37f=zY3d@E+^+(}xge6DwI z91J^YcG*cwehRU8LYP>U#WG{GRMJ*VSTQfNSQN*Wh6j8|%r^|`bZxQ=5Atl7JocD^ zMhu-)(A)Sey5AU{X3S#P-DE}Zcn^s}aqvV%5}y<=+mA-@Zsc5>j;w8UY6$iVyE;@+ I&{1yq2lQ_5kN^Mx diff --git a/_content/_hak/rune_prc8_top/nw_s1_smokeclaw.ncs b/_content/_hak/rune_prc8_top/nw_s1_smokeclaw.ncs index 85ee73d0e2645aefde45bb7a37cfc8ab67022b35..2e5a5477e2383d124a9f127ff4ed61a8c10753db 100644 GIT binary patch delta 1024 zcmZ8gZA@EL7{2G__5&L_uq_4pb#2QsptLj=3ngO%bTf)<*(M1l6Bnb~sHVUYzUF`k z5*1`+k27dc5zIDlW9Y#x>Yu8c$Y|pH(8s2+>+fuDz(RI>}XzM)`0YN1t_w92%ZwbE()b zD0SXQw+|@^$c$K_iC4LP&{B3E@;o6jRLhqw>Awr2U9P}jN0-M+S?4h7DPlKK;>pGp zkDl(H!FA6`?LFZn$u)-8i`zASB<=iZY$(pd{`?G7dYxKnOmQ~IP7C_H7s#+j&QgF@ zUo#aH@MT{jtnTsH4R<eyTAhS<4qBjY`kHR?(P1`R22a>c-<-#;a3w7)-a+_mctFL55p0=Y6S&gS z!b(uvx|xQa*(%F|3Ie9|++=6O?ZJdHh%P=A~T zBZ$3dhVzBr=;a7>1DEKfclq}Nd#&{B11OywrAP*kPtK~?r|6yfRl5hu6I;Xhw3A1t H8`S>-&(%ik delta 816 zcmW+!drXse6#ee)N2zGFEh2+H`Buk(wpgj6bTSoR(Fq!Yi!c5fCd@}vV!&3V;A3n8 zTRn@9L^Oiapk(TPTe3_OFv~V0A)mTMQ-?7`<}1cX6qhKme(fJOx%cGUbMC#leZ%}@ zh>hiCm9ABO-?aIXbn!{lc!+i*r#U0~%YPP~BuS*BcsxER$tjX_h{aDdoW;&d7^n|mj=!_1DB_SEj3!7b=)&ac4+RFd5CyyY>MH8r+_TD?s2pECSG{X8eHuO zg3^nV-uhHiB6km>n|61NlK*-m0_8;+NR*|!X@gXMg$UX#rf>pw|n{^vJrFMY#*&&8Hc z9~fd^CgzlsXtho3m^(&>8^Pqw$6#LR79I0b$#6Tur#;@F`7yp0$??mqD z!zp9Bc(h@WOzrS(9O1hXayEtdmJ=PDN;$Sbt5%7k&E+Z7jOOi~bQn|Xwvii0>gI6U zU*dLMnM{w7-S9I#gW6C|RdDPap-V7@mQp6xhKflBLTgfw*b^Ie;Z>-T=3q%!At$zl zr;tvxgzqzdFHpOuozk#-btWb?_Ru0+X>`*lh8jT*?1%)Y8|NZ!QlUn&+*wJrFT9e+ zq^ZhUn371koOoDCd(?3ROVf{ZLtJfAb=q#x{VKQiVWjgI-X5q)JJ^@t(|E50|92mt p`KxaJW>k1v*4uc?VYHq;%3G_&M?YLlp#j`E_mHceir({0`uASB9I*fZ 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 30142be84d1fdd3c0921824abbf2979117b107a9..bca10c2fb98d23c6767500ba0de84058f2ef6689 100644 GIT binary patch delta 865 zcmZ8eSxA&o6#mbd|DS(sX3A@@V8ZmzOri8}&+?t`-tU}y9(VKU$865eE_4?! zU9==ik}l1-ZyskpKPKXIO$K(~MXS-ti7Tv|JF~B}E^d>jEE%2LGu(uN> zrbO9$Ms;iO0&2lE(@A!z=r%jV7PDTVVQiZ`{Ym?tmUaixnfbcvRXgY;Du7 z+v2`RvQv^I>mgjU+u349k3GP)Tzs;7*s5T<-Of$@il9Rp)b^_12^gTn9L{K^WXVqOe%JK$|hJ2O<3&)Y%l|dRDXg5RIM&PHBST zy~6$vxas}H$s;1q#IA5V=^zXP#FL7vRkc#ke=Nb-u6uw;(Iu2#RQ|9524S8iQnunUQ zPIACio=+!GTmGFMptK@SLvB^1(=-fMY**VIk)dxOr?QZ;ajr7a>6P^nXv%D{mbDL{ zY+3YI4l>n4>}{ra+{sJ8wyN8-5&CKm{YGjv$b%=<%c%oDt35fdw5^82-z3>4X?`MR zBZ}mT>ei%53*#eV+KG=M^QNk69~s9euDMISLfQ9PN9A}^+eb$*v_Aol>ORFCiN0k_ tlTdbWE?(5%;I7*u7~E~AO``8qok|yw(elAi^)|ZUag??uih|Z!^$(Ix`w##C delta 739 zcmYL@T}YEr7{}k|yxY6YZ8F`fGxzCyH|Nl@D7+9290+=0n5h|PqO<(Mnt>P9MIR7^ zmBb(XfD}~H^dgZH70!f=sBW@|=%NqoA`uKULIShw-7LB|59j%v^E|(Q?>w82GIM#T z(o?;6cV4j|49(A&HyIlo=LWGy&)l!kXLPZQTHH5gQZkZ`reMvO!hCsiF;@^#vy&e-OIngpo4iI6FSUNxi?&}9BC@*4Zb8@~GkH#( zw;R3+8kZml@&JY%4ra~6oFmB6Te0F$m>fW&b3*U^68qsx#Up38-g`3EWGNW3rQ<!~Qe{O^MM3=0-;y~j`F<$E$V8D-;}#eou~fJq;4S-HRhCD8iD zSk?%pd>hO^#Yg-mV!m6L&KhRQ77hi*nQ{a_0zG2pGeTaG%m=gG!~z=32G6m;0+(~v zoxG#$r^MceFd|v``HB*ap;IHO!SqHCE>_vGT(w9`u}8e9ZlP`Lt0`iimA@dwpVyRY z=@M*pqZGw#U4%-JQ(wr&a(H|FmdHl0ptVt9qYdb7bdwp=jrS-C;ihukS-Y^ky}gCC zCI{X&Ez%5bhs(%@^>99E5NJN8>vyU#gfU!guB1YIZ1$$P}0rJcK0ppeL&Czcp} z!ta(Flz{84PpAYHZ3=CmyA9+*Li>K2#KCr@Xpu;@t_zwS`%aB{Xjj}8&J}VjYO7I? zB~GmWJc5BR(SDa+@#>C6QS40dV7=Im&}mMqh(v5ijI0<|zZUy4`1l2 jchF;=(R)fqcW|?BS>G0Ps{&yYGyPsT2NL;oe}nE1LL})A diff --git a/_content/_hak/rune_prc8_top/nw_s1_tyrantfga.ncs b/_content/_hak/rune_prc8_top/nw_s1_tyrantfga.ncs index 5ac4b25442920c1cbee79087eefa13f4afc36f03..5f27b264c48519d7fe22a0ec574cfacb1fe260cf 100644 GIT binary patch delta 908 zcmZ8eX-HI26n^K-8|SH6!h0{Xah&Hk(_&>#`$8#B3q@FA2Bm~clp3Yc0@WXjh@!OU zQz}T?AdJA4D@Z|Fk??6V+PsJfDN2(dau#PM0LfAA#n`;$cb1a~N!BD0fWc zKs$~&d~)D`%P`X!)P%5OAA4P*M$UB7DVto_3)ilkd2E+>;Tb@j-HE>R7S7JV z9`9)$vjst8FOU9kuwEBW3@>qTB)(*9B5d_|eu z{j$Fn63s;gPHI4DqYf&rr#skRyq=0cB{TTs1Cdt}k|`BCOWVnVsbvMU3%kocQ3tk` z&o?u-$|q9-ewQzIHG3>E&R|7FA%$?U!j~S9mS(Kl?Dwg=qZIv2$Jqb4~N6|%i(kW4L;|Yr<__}+u#WX zO8hmFbZ2HFb(-1z9`o$tyhf~XK4n6ceO%y2wd`ScIa=l2tS#ZT>@{e@MeX!oUHiq zTk&yDC!&fCd(<>mcc9;^;J7+y_I`8ev;rG8)zfD0skqp#;hvg<0ju9+i|fJ#T{{r7 zzGn5^|2`__ZQbmvK+LX*lXfQZB9>hx9%T8LcTQk2XMh6|`db|6b5yW#1uq?a>@P%> zbCgT_vFhC5l1pMacN=l>Is9C}QYvoq?kGa&(wA)A)W6S|`W_EnG$q(HPcf4!z-Q zEjs}U6-!0wI?$j;dcsi>kCl=nDVZ=GeoXVCyK7F?*Q<`q@sku}bZ_ybo_c0kREw3K g7AM6-MKoff2Y7VjySXdf9Ut)z-VAxg)KH`GACSb)>i_@% diff --git a/_content/_hak/rune_prc8_top/nw_s2_divprot.ncs b/_content/_hak/rune_prc8_top/nw_s2_divprot.ncs index b28c6e6ecc4e1a911d032ae9ce5e82ac43901948..7f7632cda770af4c9e093ac8dbfee70d9e3a04b6 100644 GIT binary patch delta 25 ecmdn7pLxfA<_+?iOdJB66*X-cL3Dyv#8v=pzXzrO delta 25 ecmdn7pLxfA<_+?iOq~3i6*X-cL3Dyv#8v=p!3U-Q diff --git a/_content/_hak/rune_prc8_top/nw_s3_balordeth.ncs b/_content/_hak/rune_prc8_top/nw_s3_balordeth.ncs index 65837869bcca471235e5c2f9be5adae752c40bf2..4e9dd36d060fcda9542d637a22c7fc6b1bbf37ab 100644 GIT binary patch delta 3231 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 E9~f{7RR910 diff --git a/_release/Rune [PRC8-CEP2].7z b/_release/Rune [PRC8-CEP2].7z deleted file mode 100644 index eddf8240a2334bf1a748544b23fb8f6d0698ff6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7452620 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∋Pl;k^46qpj|CJjTEr3LL?h*Pi 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 Wdl#f@6 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 mtE)JaCdlT%ZfiInq^Dup>jP85Dj8 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%0ACJBPK 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%0DU_V7t&a z*ef1ekxXQzE9L$vWEJ5nc~<9tLQ}!Dc}jnEniJEkrjMydl63O)4Or?6JiDSP`$0RE z-HK3ybISqF-`$eU4`2el7AU1=NFZYmb|Rs~c>Z2e{%>?H(=$ARO4C z9cn!i#SLE@vSNl)RO z2B+ULv86p>^dzmI60Igb66qHShR8w=U*|`U|N02L5rNvF6q%&uO)ym87?b25`cYW_ zu2(O0#KsTkE2A)Y4gd^TWCSxeAsizpoe%iM0c0%8T;}GdJ2Mp3%vZLrCPQ`0OourC((nod{>ou}N1IwkdIFY|q1sWfHyuIar zXm8U;{5>+{yBn;ju^YQX;Nal8EeO|&%>Hz z&IYT@+No~($Z=|vx+y#B6^u=usa`GFqpjDN;Eal;24JfK*~zXpe#Q*yERdsDuB4r> z*l}=>YqWVsYeN~E7OsX1gLK2869@#h^E$#;sQMCXlVVCs8jAammR6_O zYW4U}XBnV#hfg^v``}})5<}vQ6NNuPbE!@pGABhJmz{LQA7TQr?RbNR_ty>mM?sW{ zmtXq$kA~JBW{Rlk7xzu7vpxg6ybb^uvb3F3NIhOM8tx`7A&ph*Va3n;21oY|m=Rn) zzqShCYWK;xB*=QVTsM9&{&B>5sm^*w1qJbhC|u`9QIFh5Gnum=GoqMb3{ZK+g_Z;t zF}Dyppf%c=fSd~C0pV|jiR4gfdD{AjkzoMTqdC6Z*oKHzDFi_heJJd|0&V8i^Pd=y+5PSyTNX4GnNbm^;?!JtB#-0;nbf*Q~x~JmzgcfGs2*Kc`_#GQY(pSYX)r@agy_ zUKGil&j76y=%M7Z5n<~7`h12#0rqx>rf)QA@-%f^c(;NtQM}o(owjukxNFDMkEkD% z;sA=1RvVZ)Bw2>#7-KI9G^2qloL}c_

rN#6}`7kf8Vg&qZNv`SWCOV0C;*-2e`( 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