From fc7c3485be555c56d092d8c1268ec5925d5dd28b Mon Sep 17 00:00:00 2001 From: Jaysyn904 <68194417+Jaysyn904@users.noreply.github.com> Date: Wed, 24 Dec 2025 22:10:16 -0500 Subject: [PATCH] 2025/12/24 Update Hooked up new GUI module event. Updated PEPS. Updated PRC8 includes. Updated nasher.cfg. --- nasher.cfg | 12 +- src/include/inc_dynconv.nss | 60 +- src/include/inc_epicspellfnc.nss | 2 +- src/include/inc_epicspells.nss | 11 +- src/include/inc_item_props.nss | 55 +- src/include/inc_newspellbook.nss | 12 +- src/include/inc_npc.nss | 178 ++- src/include/inc_rand_equip.nss | 1 + src/include/inc_rend.nss | 1 + src/include/inc_sp_gain_mem.nss | 11 +- src/include/inc_switch_setup.nss | 104 +- src/include/inv_inc_blast.nss | 1 + src/include/inv_inc_invknown.nss | 3 + src/include/inv_inc_invoke.nss | 2 +- src/include/inv_invokehook.nss | 10 + src/include/moi_inc_moifunc.nss | 5 +- src/include/pnp_shft_poly.nss | 585 ++++++++- src/include/prc_add_spl_pen.nss | 4 +- src/include/prc_craft_inc.nss | 23 +- src/include/prc_effect_inc.nss | 39 +- src/include/prc_feat_const.nss | 13 + src/include/prc_getbest_inc.nss | 3 +- src/include/prc_inc_breath.nss | 2 + src/include/prc_inc_castlvl.nss | 62 +- src/include/prc_inc_chat_pow.nss | 1 + src/include/prc_inc_clsfunc.nss | 5 +- src/include/prc_inc_combat.nss | 26 +- src/include/prc_inc_combmove.nss | 40 +- src/include/prc_inc_core.nss | 3 + src/include/prc_inc_descrptr.nss | 4 +- src/include/prc_inc_factotum.nss | 22 +- src/include/prc_inc_fork.nss | 91 +- src/include/prc_inc_function.nss | 4 +- src/include/prc_inc_hextor.nss | 2 + src/include/prc_inc_itmrstr.nss | 2 + src/include/prc_inc_json.nss | 1277 ++++++++++++++++++- src/include/prc_inc_material.nss | 4 +- src/include/prc_inc_nat_hb.nss | 264 +++- src/include/prc_inc_natweap.nss | 26 + src/include/prc_inc_nwscript.nss | 5 +- src/include/prc_inc_onhit.nss | 21 +- src/include/prc_inc_shifting.nss | 4 +- src/include/prc_inc_size.nss | 154 +++ src/include/prc_inc_skills.nss | 7 +- src/include/prc_inc_spells.nss | 5 + src/include/prc_inc_switch.nss | 135 +- src/include/prc_inc_turning.nss | 2 + src/include/prc_inc_unarmed.nss | 402 ++++-- src/include/prc_inc_wpnrest.nss | 116 +- src/include/prc_ipfeat_const.nss | 2 +- src/include/prc_misc_const.nss | 4 +- src/include/prc_nui_com_inc.nss | 85 ++ src/include/prc_nui_consts.nss | 13 + src/include/prc_nui_lv_inc.nss | 125 +- src/include/prc_nui_sbd_inc.nss | 10 +- src/include/prc_sp_func.nss | 18 + src/include/prc_spell_const.nss | 30 +- src/include/prc_spellf_inc.nss | 3 +- src/include/prc_weap_apt.nss | 4 +- src/include/prc_x2_craft.nss | 219 +++- src/include/prc_x2_itemprop.nss | 158 ++- src/include/prcsp_archmaginc.nss | 1 + src/include/psi_inc_ac_spawn.nss | 1 + src/include/psi_inc_core.nss | 398 ++++-- src/include/psi_inc_metapsi.nss | 1 + src/include/psi_inc_powknown.nss | 6 +- src/include/psi_inc_ppoints.nss | 11 +- src/include/psi_inc_psicraft.nss | 3 +- src/include/psi_inc_psifunc.nss | 30 +- src/include/psi_inc_pwresist.nss | 1 + src/include/psi_spellhook.nss | 10 + src/include/shd_inc_mystknwn.nss | 5 +- src/include/shd_inc_shdfunc.nss | 1 + src/include/shd_mysthook.nss | 15 +- src/include/tob_movehook.nss | 10 +- src/include/true_inc_truespk.nss | 3 + src/include/true_inc_trufunc.nss | 1 + src/include/true_inc_truknwn.nss | 1 + src/include/true_utterhook.nss | 11 + src/include/x2_inc_spellhook.nss | 4 +- src/include/x3_inc_horse.nss | 5 +- src/module/dlg/oc_ai_henchmen.dlg.json | 498 ++++++-- src/module/ifo/module.ifo.json | 2 +- src/module/nss/0c_assoc_actions.nss | 36 +- src/module/nss/0c_cast_polymorp.nss | 3 - src/module/nss/0c_get_convo.nss | 2 + src/module/nss/0c_henchmenspell.nss | 1 + src/module/nss/0c_if_ass_convo.nss | 2 +- src/module/nss/0c_if_hen_target.nss | 16 + src/module/nss/0c_if_not_master.nss | 6 +- src/module/nss/0c_if_scout.nss | 7 +- src/module/nss/0e_animations.nss | 26 + src/module/nss/0e_c2_7_ondeath.nss | 6 +- src/module/nss/0e_ch_7_ondeath.nss | 18 +- src/module/nss/0e_nui.nss | 402 +++--- src/module/nss/0e_nui_dm.nss | 24 +- src/module/nss/0e_onclientload.nss | 6 +- src/module/nss/0e_player_target.nss | 107 +- src/module/nss/0e_prc_id_events.nss | 317 +++++ src/module/nss/0i_actions.nss | 197 ++- src/module/nss/0i_associates.nss | 104 +- src/module/nss/0i_combat.nss | 1623 ++++++++++++++++++------ src/module/nss/0i_constants.nss | 89 +- src/module/nss/0i_gui_events.nss | 2 +- src/module/nss/0i_main.nss | 108 +- src/module/nss/0i_menus.nss | 1044 +++++++++------ src/module/nss/0i_menus_dm.nss | 64 +- src/module/nss/0i_module.nss | 72 +- src/module/nss/0i_player_target.nss | 213 +--- src/module/nss/0i_spells.nss | 402 +++++- src/module/nss/0i_states_cond.nss | 22 +- src/module/nss/0i_talents.nss | 1389 +++++++++++++++++--- src/module/nss/0i_time.nss | 64 +- src/module/nss/ai_a_atk_casters.nss | 10 +- src/module/nss/ai_a_atk_warrior.nss | 10 +- src/module/nss/ai_a_barbarian.nss | 6 +- src/module/nss/ai_a_bard.nss | 6 +- src/module/nss/ai_a_cleric.nss | 6 +- src/module/nss/ai_a_default.nss | 18 +- src/module/nss/ai_a_defensive.nss | 2 +- src/module/nss/ai_a_druid.nss | 56 +- src/module/nss/ai_a_fighter.nss | 6 +- src/module/nss/ai_a_flanker.nss | 14 +- src/module/nss/ai_a_invisible.nss | 6 +- src/module/nss/ai_a_monk.nss | 6 +- src/module/nss/ai_a_no_cmb_mode.nss | 6 +- src/module/nss/ai_a_paladin.nss | 6 +- src/module/nss/ai_a_polymorphed.nss | 26 +- src/module/nss/ai_a_ranged.nss | 38 +- src/module/nss/ai_a_ranger.nss | 6 +- src/module/nss/ai_a_rogue.nss | 6 +- src/module/nss/ai_a_sorcerer.nss | 4 +- src/module/nss/ai_a_wizard.nss | 4 +- src/module/nss/ai_barbarian.nss | 6 +- src/module/nss/ai_bard.nss | 6 +- src/module/nss/ai_bloodmane.nss | 38 + src/module/nss/ai_cleric.nss | 4 +- src/module/nss/ai_default.nss | 9 +- src/module/nss/ai_dragon_boss.nss | 103 ++ src/module/nss/ai_druid.nss | 41 +- src/module/nss/ai_fighter.nss | 8 +- src/module/nss/ai_flanker.nss | 6 +- src/module/nss/ai_incorporeal.nss | 14 +- src/module/nss/ai_invisible.nss | 6 +- src/module/nss/ai_monk.nss | 4 +- src/module/nss/ai_paladin.nss | 4 +- src/module/nss/ai_polymorphed.nss | 52 +- src/module/nss/ai_ranged.nss | 38 +- src/module/nss/ai_ranger.nss | 6 +- src/module/nss/ai_rogue.nss | 2 +- src/module/nss/ai_shadow.nss | 6 +- src/module/nss/ai_sorcerer.nss | 4 +- src/module/nss/ai_taunter.nss | 6 +- src/module/nss/ai_wizard.nss | 4 +- src/module/nss/mm_prc_spells.nss | 2 +- src/module/nss/nw_c2_default1.nss | 19 +- src/module/nss/nw_c2_default2.nss | 2 +- src/module/nss/nw_c2_default3.nss | 6 +- src/module/nss/nw_c2_default4.nss | 5 +- src/module/nss/nw_c2_default5.nss | 2 +- src/module/nss/nw_c2_default6.nss | 4 +- src/module/nss/nw_c2_default8.nss | 8 +- src/module/nss/nw_c2_defaultb.nss | 2 +- src/module/nss/nw_c2_defaulte.nss | 1 + src/module/nss/nw_ch_ac1.nss | 2 + src/module/nss/nw_ch_ac3.nss | 8 + src/module/nss/nw_ch_acb.nss | 5 +- src/module/nss/pc_savebuffs.nss | 127 +- src/module/nss/pe_buffing.nss | 197 +-- src/module/nss/pe_crafting.nss | 953 ++++++++++++-- src/module/nss/pe_debug.nss | 20 +- src/module/nss/pe_henchmen.nss | 2 + src/module/nss/pi_buffing.nss | 116 +- src/module/nss/pi_crafting.nss | 687 +--------- src/module/nss/pi_debug.nss | 38 +- src/module/nss/pinc_henchmen.nss | 158 ++- 176 files changed, 11289 insertions(+), 3566 deletions(-) create mode 100644 src/include/prc_inc_size.nss create mode 100644 src/module/nss/0c_if_hen_target.nss create mode 100644 src/module/nss/0e_animations.nss create mode 100644 src/module/nss/0e_prc_id_events.nss create mode 100644 src/module/nss/ai_bloodmane.nss create mode 100644 src/module/nss/ai_dragon_boss.nss diff --git a/nasher.cfg b/nasher.cfg index d73ff5f..37be4dd 100644 --- a/nasher.cfg +++ b/nasher.cfg @@ -21,6 +21,7 @@ description = "PRC8 version of D3 - Vault of the Drow." include = "src/module/**/*" include = "src/include/**/*" filter = "bnd_inc_bndfunc.nss" + filter = "bnd_inc_bndfunc.nss" filter = "bnd_vestig_const.nss" filter = "inc_2dacache.nss" filter = "inc_abil_damage.nss" @@ -231,9 +232,16 @@ description = "PRC8 version of D3 - Vault of the Drow." filter = "prc_nui_sc_inc.nss" filter = "prc_nui_scd_inc.nss" filter = "prc_nui_consts.nss" - filter = "nw_inc_nui" + filter = "nw_inc_nui.nss" filter = "xchst_inc.nss" - + filter = "prc_nui_sbd_inc.nss" + filter = "prc_nui_sb_inc.nss" + filter = "prc_nui_lv_inc.nss" + filter = "prc_nui_com_inc.nss" + filter = "prc_inc_size.nss" + filter = "prc_inc_json.nss" + filter = "prc_inc_gff.nss" + filter = "inc_infusion.nss" [target.rules] "*" = "src/module/$ext" diff --git a/src/include/inc_dynconv.nss b/src/include/inc_dynconv.nss index 4603fc4..68aabac 100644 --- a/src/include/inc_dynconv.nss +++ b/src/include/inc_dynconv.nss @@ -12,6 +12,7 @@ //::////////////////////////////////////////////// //::////////////////////////////////////////////// +//PRC8 Token pre-fix = 161838 ////////////////////////////////////////////////// /* Constant definitions */ @@ -21,23 +22,23 @@ const int DYNCONV_EXITED = -2; const int DYNCONV_ABORTED = -3; const int DYNCONV_SETUP_STAGE = -1; -const int DYNCONV_TOKEN_HEADER = 99; -const int DYNCONV_TOKEN_REPLY_0 = 100; -const int DYNCONV_TOKEN_REPLY_1 = 101; -const int DYNCONV_TOKEN_REPLY_2 = 102; -const int DYNCONV_TOKEN_REPLY_3 = 103; -const int DYNCONV_TOKEN_REPLY_4 = 104; -const int DYNCONV_TOKEN_REPLY_5 = 105; -const int DYNCONV_TOKEN_REPLY_6 = 106; -const int DYNCONV_TOKEN_REPLY_7 = 107; -const int DYNCONV_TOKEN_REPLY_8 = 108; -const int DYNCONV_TOKEN_REPLY_9 = 109; -const int DYNCONV_TOKEN_EXIT = 110; -const int DYNCONV_TOKEN_WAIT = 111; -const int DYNCONV_TOKEN_NEXT = 112; -const int DYNCONV_TOKEN_PREV = 113; -const int DYNCONV_MIN_TOKEN = 99; -const int DYNCONV_MAX_TOKEN = 113; +const int DYNCONV_TOKEN_HEADER = 16183899; +const int DYNCONV_TOKEN_REPLY_0 = 161838100; +const int DYNCONV_TOKEN_REPLY_1 = 161838101; +const int DYNCONV_TOKEN_REPLY_2 = 161838102; +const int DYNCONV_TOKEN_REPLY_3 = 161838103; +const int DYNCONV_TOKEN_REPLY_4 = 161838104; +const int DYNCONV_TOKEN_REPLY_5 = 161838105; +const int DYNCONV_TOKEN_REPLY_6 = 161838106; +const int DYNCONV_TOKEN_REPLY_7 = 161838107; +const int DYNCONV_TOKEN_REPLY_8 = 161838108; +const int DYNCONV_TOKEN_REPLY_9 = 161838109; +const int DYNCONV_TOKEN_EXIT = 161838110; +const int DYNCONV_TOKEN_WAIT = 161838111; +const int DYNCONV_TOKEN_NEXT = 161838112; +const int DYNCONV_TOKEN_PREV = 161838113; +const int DYNCONV_MIN_TOKEN = 16183899; +const int DYNCONV_MAX_TOKEN = 161838113; const int DYNCONV_STRREF_PLEASE_WAIT = 16824202; // "Please wait" const int DYNCONV_STRREF_PREVIOUS = 16824203; // "Previous" @@ -477,9 +478,28 @@ void _DynConvInternal_ExitedConvo(object oPC, int bAbort) DeleteLocalInt(oPC, DYNCONV_STAGE); DeleteLocalString(oPC, DYNCONV_SCRIPT); DeleteLocalString(oPC, "DynConv_HeaderText"); - int i; - for(i = DYNCONV_MIN_TOKEN; i <= DYNCONV_MAX_TOKEN; i++) - DeleteLocalString(oPC, GetTokenIDString(i)); + + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_HEADER)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_0)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_1)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_2)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_3)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_4)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_5)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_6)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_7)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_8)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_REPLY_9)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_EXIT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_WAIT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_NEXT)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_TOKEN_PREV)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_MIN_TOKEN)); + DeleteLocalString(oPC, GetTokenIDString(DYNCONV_MAX_TOKEN)); + + //int i; + //for(i = DYNCONV_MIN_TOKEN; i <= DYNCONV_MAX_TOKEN; i++) + //DeleteLocalString(oPC, GetTokenIDString(i)); } } } diff --git a/src/include/inc_epicspellfnc.nss b/src/include/inc_epicspellfnc.nss index 7fb6054..4537d45 100644 --- a/src/include/inc_epicspellfnc.nss +++ b/src/include/inc_epicspellfnc.nss @@ -26,7 +26,7 @@ int GetSpellFromAbrev(string sAbrev); ////////////////////////////////////////////////// #include "inc_utility" -//#include "inc_epicspelldef" +#include "inc_epicspells" // SEED FUNCTIONS diff --git a/src/include/inc_epicspells.nss b/src/include/inc_epicspells.nss index ec3eac9..4682672 100644 --- a/src/include/inc_epicspells.nss +++ b/src/include/inc_epicspells.nss @@ -303,11 +303,18 @@ int GetIsEpicShaman(object oPC) && GetAbilityScore(oPC, ABILITY_WISDOM) > 18; } -int GetIsEpicSorcerer(object oPC) +int GetIsEpicSorcerer(object oPC) +{ + return GetHitDice(oPC) >= 21 + && GetCasterLvl(CLASS_TYPE_SORCERER, oPC) > 17 + && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; +} + +/* int GetIsEpicSorcerer(object oPC) { return GetPrCAdjustedCasterLevel(CLASS_TYPE_SORCERER, oPC, FALSE) > 17 && GetAbilityScore(oPC, ABILITY_CHARISMA) > 18; -} +} */ int GetIsEpicSublimeChord(object oPC) { diff --git a/src/include/inc_item_props.nss b/src/include/inc_item_props.nss index b1edfc2..b912fad 100644 --- a/src/include/inc_item_props.nss +++ b/src/include/inc_item_props.nss @@ -1643,7 +1643,60 @@ int GetIsMagicItem(object oItem) int FeatToIprop(int nFeat) { switch(nFeat) - {//: Weapon Focus + { + //:: Weapon Proficiencies + case FEAT_WEAPON_PROFICIENCY_SHORTSWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTSWORD; + case FEAT_WEAPON_PROFICIENCY_LONGSWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LONGSWORD; + case FEAT_WEAPON_PROFICIENCY_BATTLEAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_BATTLEAXE; + case FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD; + case FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL; + case FEAT_WEAPON_PROFICIENCY_WARHAMMER: return IP_CONST_FEAT_WEAPON_PROFICIENCY_WARHAMMER; + case FEAT_WEAPON_PROFICIENCY_LONGBOW: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LONGBOW; + case FEAT_WEAPON_PROFICIENCY_LIGHT_MACE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_MACE; + case FEAT_WEAPON_PROFICIENCY_HALBERD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HALBERD; + case FEAT_WEAPON_PROFICIENCY_SHORTBOW: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTBOW; + case FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD; + case FEAT_WEAPON_PROFICIENCY_GREATSWORD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_GREATSWORD; + case FEAT_WEAPON_PROFICIENCY_GREATAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_GREATAXE; + case FEAT_WEAPON_PROFICIENCY_DART: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DART; + case FEAT_WEAPON_PROFICIENCY_DIRE_MACE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DIRE_MACE; + case FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE; + case FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL; + case FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER; + case FEAT_WEAPON_PROFICIENCY_HANDAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HANDAXE; + case FEAT_WEAPON_PROFICIENCY_KAMA: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KAMA; + case FEAT_WEAPON_PROFICIENCY_KATANA: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KATANA; + case FEAT_WEAPON_PROFICIENCY_KUKRI: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KUKRI; + case FEAT_WEAPON_PROFICIENCY_MORNINGSTAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_MORNINGSTAR; + case FEAT_WEAPON_PROFICIENCY_RAPIER: return IP_CONST_FEAT_WEAPON_PROFICIENCY_RAPIER; + case FEAT_WEAPON_PROFICIENCY_SCIMITAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SCIMITAR; + case FEAT_WEAPON_PROFICIENCY_SCYTHE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SCYTHE; + case FEAT_WEAPON_PROFICIENCY_SHORTSPEAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHORTSPEAR; + case FEAT_WEAPON_PROFICIENCY_SHURIKEN: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SHURIKEN; + case FEAT_WEAPON_PROFICIENCY_SICKLE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SICKLE; + case FEAT_WEAPON_PROFICIENCY_SLING: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SLING; + case FEAT_WEAPON_PROFICIENCY_THROWING_AXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_THROWING_AXE; + case FEAT_WEAPON_PROFICIENCY_TRIDENT: return IP_CONST_FEAT_WEAPON_PROFICIENCY_TRIDENT; + case FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE; + case FEAT_WEAPON_PROFICIENCY_WHIP: return IP_CONST_FEAT_WEAPON_PROFICIENCY_WHIP; + case FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE; + case FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE; + case FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE; + case FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE; + case FEAT_WEAPON_PROFICIENCY_HEAVY_PICK: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_PICK; + case FEAT_WEAPON_PROFICIENCY_LIGHT_PICK: return IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_PICK; + case FEAT_WEAPON_PROFICIENCY_SAI: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SAI; + case FEAT_WEAPON_PROFICIENCY_NUNCHAKU: return IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHAKU; + case FEAT_WEAPON_PROFICIENCY_FALCHION: return IP_CONST_FEAT_WEAPON_PROFICIENCY_FALCHION; + case FEAT_WEAPON_PROFICIENCY_SAP: return IP_CONST_FEAT_WEAPON_PROFICIENCY_SAP; + case FEAT_WEAPON_PROFICIENCY_KATAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_KATAR; + case FEAT_WEAPON_PROFICIENCY_HEAVY_MACE: return IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_MACE; + case FEAT_WEAPON_PROFICIENCY_MAUL: return IP_CONST_FEAT_WEAPON_PROFICIENCY_MAUL; + case FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR: return IP_CONST_FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR; + case FEAT_WEAPON_PROFICIENCY_GOAD: return IP_CONST_FEAT_WEAPON_PROFICIENCY_GOAD; + case FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW: return IP_CONST_FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW; + + //: Weapon Focus case FEAT_WEAPON_FOCUS_BASTARD_SWORD: return IP_CONST_FEAT_WEAPON_FOCUS_BASTARD_SWORD; case FEAT_WEAPON_FOCUS_BATTLE_AXE: return IP_CONST_FEAT_WEAPON_FOCUS_BATTLE_AXE; case FEAT_WEAPON_FOCUS_CLUB: return IP_CONST_FEAT_WEAPON_FOCUS_CLUB; diff --git a/src/include/inc_newspellbook.nss b/src/include/inc_newspellbook.nss index 542e2d5..5a06b4d 100644 --- a/src/include/inc_newspellbook.nss +++ b/src/include/inc_newspellbook.nss @@ -20,6 +20,8 @@ Add class to GetCasterLvl() in prc_inc_spells Add Practiced Spellcaster feat to feat.2da and to PracticedSpellcasting() in prc_inc_castlvl Run the assemble_spellbooks.bat file Make the prc_* scripts in newspellbook. The filenames can be found under the spell entries for the class in spells.2da. +Update the fileends for all relevant files in inc_switch_setup +Delete prc_data in the \database\ folder before testing new spells. Spont: Make cls_spgn_*.2da @@ -41,6 +43,8 @@ Add class to prc_amagsys_gain if(CheckMissingSpells(oPC, CLASS_TYPE_SORCERER, Mi Add class to ExecuteScript("prc_amagsys_gain", oPC) list in EvalPRCFeats in prc_inc_function Run the assemble_spellbooks.bat file Make the prc_* scripts in newspellbook +Update the fileends for all relevant files in inc_switch_setup +Delete prc_data in the \database\ folder before testing new spells. prc_classes.2da entry: Label - name for the class @@ -104,11 +108,10 @@ void ProcessPreparedSpellLevel(object oPC, int nClass, int nSpellLevel, int nLev //#include "prc_effect_inc" //access via prc_inc_core //#include "inc_lookups" //access via prc_inc_core #include "prc_inc_core" -#include "inc_sp_gain_mem" //providing child access to prc_inc_core - //Must load in this order. +#include "inc_sp_gain_mem" //#include "prc_inc_castlvl" //access via prc_inc_core //#include "prc_inc_descrptr" //access via prc_inc_core - +#include "inc_item_props" ////////////////////////////////////////////////// /* Function definitions */ @@ -1618,6 +1621,3 @@ void DoCleanUp(int nMetamagic) DeleteLocalInt(OBJECT_SELF, "NSB_SpellLevel"); DeleteLocalInt(OBJECT_SELF, "NSB_SpellbookID"); } - -//:: Test Void -//:: void main (){} \ No newline at end of file diff --git a/src/include/inc_npc.nss b/src/include/inc_npc.nss index 6b99a4a..8385ddc 100644 --- a/src/include/inc_npc.nss +++ b/src/include/inc_npc.nss @@ -7,8 +7,11 @@ //::////////////////////////////////////////////// //::////////////////////////////////////////////// +//:: Levels up an NPC according to variables set on NPC. +void LevelUpSummon(object oSummon, int iTargetLvl); + // Get the master of oAssociate. -object GetMasterNPC(object oAssociate=OBJECT_SELF); +object GetMasterNPC(object oAssociate0 = OBJECT_SELF); // Returns the associate type of the specified creature. // - Returns ASSOCIATE_TYPE_NONE if the creature is not the associate of anyone. @@ -75,7 +78,6 @@ void DestroySummon(object oSummon) DestroyObject(oSummon); } - object CreateLocalNPC(object oMaster,int nAssociateType,string sTemplate,location loc,int Nth=1,string sTag="") { object oSummon=CreateObject(OBJECT_TYPE_CREATURE,sTemplate,loc,FALSE,sTag); @@ -111,7 +113,7 @@ object CreateLocalNextNPC(object oMaster,int nAssociateType,string sTemplate,loc SetLocalObject(oMaster, IntToString(nAssociateType)+"oHench"+IntToString(nCount), oSummon); SetLocalInt(oSummon, "iAssocNth", nCount); - SetAssociateState(NW_ASC_HAVE_MASTER,TRUE,oSummon); + SetAssociateState(NW_ASC_HAVE_MASTER, TRUE, oSummon); SetAssociateState(NW_ASC_DISTANCE_2_METERS); SetAssociateState(NW_ASC_DISTANCE_4_METERS, FALSE); SetAssociateState(NW_ASC_DISTANCE_6_METERS, FALSE); @@ -122,6 +124,7 @@ object CreateLocalNextNPC(object oMaster,int nAssociateType,string sTemplate,loc return oSummon; } + object GetMasterNPC(object oAssociate=OBJECT_SELF) { object oMaster = GetLocalObject(oAssociate, "oMaster"); @@ -220,4 +223,173 @@ int GetAssociateHealMasterNPC() return FALSE; } +/** + * @brief Levels up a summoned creature based on its master's total casting level, + * while respecting configured HD limits and multiclass transition rules. + * Should only be called on the NPC onSpawn event. + * + * This function: + * - Retrieves the master’s total casting level and clamps it to the creature’s + * minimum and maximum HD (iMinHD, iMaxHD). + * - Repeatedly calls LevelUpHenchman() until the creature reaches that level, + * switching classes when the creature's stored "ClassXStart" thresholds are met. + * + * Local variables recognized on the summoned creature: + * + * | Variable Name | Purpose | + * |-----------------|-------------------------------------------------------------| + * | iMinHD | Minimum HD allowed | + * | iMaxHD | Maximum HD allowed | + * | Class2Start | Level to begin second class progression | + * | Class2 | Class type for second progression | + * | Class2Package | Package for second progression | + * | Class3Start | Level to begin third class progression | + * | Class3 | Class type for third progression | + * | Class3Package | Package for third progression | + * | Class4Start | Level to begin fourth class progression | + * | Class4 | Class type for fourth progression | + * | Class4Package | Package for fourth progression | + * + * Behavior notes: + * - Leveling continues until the creature reaches the master’s effective + * casting level (bounded by iMinHD/iMaxHD). + * - If LevelUpHenchman() returns 0, the creature shouts a failure message. + * - CLASS_TYPE_INVALID causes the creature to level in its current class. + * + * @param oCreature The summoned creature being leveled. Defaults to OBJECT_SELF. + * + * @see LevelUpHenchman + * @see GetLocalInt + * @see GetHitDice + */ +void LevelUpSummon(object oSummon, int iTargetLvl) +{ + int nCurrentHD = GetHitDice(oSummon); + int iNewHD = nCurrentHD; + // Read multiclassing info from locals + int iClass2Start = GetLocalInt(oSummon, "Class2Start"); + int iClass2 = GetLocalInt(oSummon, "Class2"); + int iClass2Package = GetLocalInt(oSummon, "Class2Package"); + + int iClass3Start = GetLocalInt(oSummon, "Class3Start"); + int iClass3 = GetLocalInt(oSummon, "Class3"); + int iClass3Package = GetLocalInt(oSummon, "Class3Package"); + + int iClass4Start = GetLocalInt(oSummon, "Class4Start"); + int iClass4 = GetLocalInt(oSummon, "Class4"); + int iClass4Package = GetLocalInt(oSummon, "Class4Package"); + + int iClass; // current class to level + int iPackage; // package to use + + // Main leveling loop + while (nCurrentHD < iTargetLvl && nCurrentHD > 0) + { + // Determine which class and package to use + if (iClass4Start != 0 && nCurrentHD >= iClass4Start) + { + iClass = iClass4; + iPackage = iClass4Package; + } + else if (iClass3Start != 0 && nCurrentHD >= iClass3Start) + { + iClass = iClass3; + iPackage = iClass3Package; + } + else if (iClass2Start != 0 && nCurrentHD >= iClass2Start) + { + iClass = iClass2; + iPackage = iClass2Package; + } + else + { + // Base class (first class in the sheet) + iClass = CLASS_TYPE_INVALID; // keeps current + iPackage = PACKAGE_INVALID; + } + + // Level up one HD + iNewHD = LevelUpHenchman(oSummon, iClass, TRUE, iPackage); + + if (iNewHD == 0) + { + SpeakString(GetName(oSummon) + " failed to level properly!", TALKVOLUME_SHOUT); + break; + } + + nCurrentHD = iNewHD; + } + + // Force the creature to rest to memorize spells + // PRCForceRest(oSummon); + +} + + + + + +/* void LevelUpSummon(object oSummon, int iTargetLvl) +{ + //get the default hit dice of the summon + int nDefaultHD = GetHitDice(oSummon); + + if (DEBUG) DoDebug("inc_npc >> LevelUpSummon: nDefaultHD = " +IntToString(nDefaultHD)+"."); + + if (DEBUG) DoDebug("inc_npc >> LevelUpSummon: iTargetLvl = " +IntToString(iTargetLvl)+"."); + + //get the multiclassing variables to see if we need to change classes from its base class + int iClass2Start = GetLocalInt(oSummon, "Class2Start"); + int iClass2 = GetLocalInt(oSummon, "Class2"); + int iClass2Package = GetLocalInt(oSummon, "Class2Package"); + + int iClass3Start = GetLocalInt(oSummon, "Class3Start"); + int iClass3 = GetLocalInt(oSummon, "Class3"); + int iClass3Package = GetLocalInt(oSummon, "Class3Package"); + + int iClass4Start = GetLocalInt(oSummon, "Class4Start"); + int iClass4 = GetLocalInt(oSummon, "Class4"); + int iClass4Package = GetLocalInt(oSummon, "Class4Package"); + + //check for zero cause thats an error + //if creatures are not leveling then best bet is they are not legal creatures + while( (nDefaultHD < iTargetLvl) && (nDefaultHD > 0) ) + { + //check the multiclassing numbers to change classes + if( (iClass4Start != 0) && (nDefaultHD >= iClass4Start) ) + { + //level up using the new class and Packageage + nDefaultHD = LevelUpHenchman(oSummon, iClass4 ,TRUE, iClass4Package); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed on fourth class", TALKVOLUME_SHOUT); + } + else if( (iClass3Start != 0) && (nDefaultHD >= iClass3Start) ) + { + //level up using the new class and Packageage + nDefaultHD = LevelUpHenchman(oSummon, iClass3 ,TRUE, iClass3Package); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed on third class", TALKVOLUME_SHOUT); + } + else if( (iClass2Start != 0) && (nDefaultHD >= iClass2Start) ) + { + //level up using the new class and Packageage + nDefaultHD = LevelUpHenchman(oSummon, iClass2 ,TRUE, iClass2Package); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed on second class", TALKVOLUME_SHOUT); + } + else + { + //just level up using the class it already has + nDefaultHD = LevelUpHenchman(oSummon, CLASS_TYPE_INVALID ,TRUE); + + if(nDefaultHD == 0) + SpeakString(GetName(oSummon) + " Failed to level properly", TALKVOLUME_SHOUT); + } + } +} + */ +//:: void main() {} \ No newline at end of file diff --git a/src/include/inc_rand_equip.nss b/src/include/inc_rand_equip.nss index 814f533..07f7191 100644 --- a/src/include/inc_rand_equip.nss +++ b/src/include/inc_rand_equip.nss @@ -3433,6 +3433,7 @@ int PrimoGetWeaponSize(object oItem) case BASE_ITEM_LIGHTFLAIL: case BASE_ITEM_KATANA: case BASE_ITEM_MAGICSTAFF: + case BASE_ITEM_CRAFTED_SCEPTER: case BASE_ITEM_LONGSWORD: case BASE_ITEM_TRIDENT: case BASE_ITEM_MORNINGSTAR: diff --git a/src/include/inc_rend.nss b/src/include/inc_rend.nss index 06e2b2f..07ae6b3 100644 --- a/src/include/inc_rend.nss +++ b/src/include/inc_rend.nss @@ -30,6 +30,7 @@ int GetDamageFromConstant(int nIPConst); void DoFrostRend(object oTarget, object oAttacker, object oWeapon); #include "moi_inc_moifunc" +#include "prc_inc_combat" ////////////////////////////////////////////////// /* Function defintions */ diff --git a/src/include/inc_sp_gain_mem.nss b/src/include/inc_sp_gain_mem.nss index d994896..b3981be 100644 --- a/src/include/inc_sp_gain_mem.nss +++ b/src/include/inc_sp_gain_mem.nss @@ -17,9 +17,6 @@ Created: May 1, 2008 //:: Updated for .35 by Jaysyn 2023/03/11 -//:: Test Void -//void main (){} - //::////////////////////////////////////////////// //:: Constants //::////////////////////////////////////////////// @@ -63,6 +60,14 @@ string GetMetaMagicString(int nMetaMagic); int GetMetaMagicFromFeat(int nFeat); int GetMetaMagicOfCaster(object oPC = OBJECT_SELF); +string GetFileForClass(int nClass); +int GetSpellslotLevel(int nClass, object oPC); +int GetSpellKnownMaxCount(int nLevel, int nSpellLevel, int nClass, object oPC); +int GetSpellbookTypeForClass(int nClass); + +#include "inc_pers_array" +#include "inc_2dacache" + // name of the new spellbook file (cls_spell_*) string GetNSBDefinitionFileName(int nClass) { diff --git a/src/include/inc_switch_setup.nss b/src/include/inc_switch_setup.nss index d21b16c..86c526c 100644 --- a/src/include/inc_switch_setup.nss +++ b/src/include/inc_switch_setup.nss @@ -44,6 +44,7 @@ void CreateSwitchNameArray(); #include "prc_inc_array" // Needs direct include instead of inc_utility #include "prc_inc_switch" +#include "inc_2dacache" ////////////////////////////////////////////////// /* Function definitions */ @@ -221,9 +222,9 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_cls_spcr_bard", 144); SetPRCSwitch("PRC_FILE_END_cls_spcr_beguil", 142); SetPRCSwitch("PRC_FILE_END_cls_spcr_blkgrd", 47); - SetPRCSwitch("PRC_FILE_END_cls_spcr_dnecro", 137); + SetPRCSwitch("PRC_FILE_END_cls_spcr_dnecro", 138); SetPRCSwitch("PRC_FILE_END_cls_spcr_duskbl", 69); - SetPRCSwitch("PRC_FILE_END_cls_spcr_favsol", 290); + SetPRCSwitch("PRC_FILE_END_cls_spcr_favsol", 300); SetPRCSwitch("PRC_FILE_END_cls_spcr_harper", 35); SetPRCSwitch("PRC_FILE_END_cls_spcr_healer", 77); SetPRCSwitch("PRC_FILE_END_cls_spcr_hexbl", 73); @@ -251,9 +252,9 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_cls_spell_bard", 169); SetPRCSwitch("PRC_FILE_END_cls_spell_beguil", 119); SetPRCSwitch("PRC_FILE_END_cls_spell_blkgrd", 163); - SetPRCSwitch("PRC_FILE_END_cls_spell_dnecro", 134); + SetPRCSwitch("PRC_FILE_END_cls_spell_dnecro", 135); SetPRCSwitch("PRC_FILE_END_cls_spell_duskbl", 84); - SetPRCSwitch("PRC_FILE_END_cls_spell_favsol", 363); + SetPRCSwitch("PRC_FILE_END_cls_spell_favsol", 373); SetPRCSwitch("PRC_FILE_END_cls_spell_harper", 21); SetPRCSwitch("PRC_FILE_END_cls_spell_healer", 271); SetPRCSwitch("PRC_FILE_END_cls_spell_hexbl", 79); @@ -267,7 +268,7 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_cls_spell_sod", 110); SetPRCSwitch("PRC_FILE_END_cls_spell_sohei", 131); SetPRCSwitch("PRC_FILE_END_cls_spell_sol", 114); - SetPRCSwitch("PRC_FILE_END_cls_spell_sorc", 541); + SetPRCSwitch("PRC_FILE_END_cls_spell_sorc", 550); SetPRCSwitch("PRC_FILE_END_cls_spell_suel", 160); SetPRCSwitch("PRC_FILE_END_cls_spell_templ", 95); SetPRCSwitch("PRC_FILE_END_cls_spell_tfshad", 70); @@ -335,7 +336,7 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_craft_golem", 40); SetPRCSwitch("PRC_FILE_END_craft_ring", 41); SetPRCSwitch("PRC_FILE_END_craft_weapon", 46); - SetPRCSwitch("PRC_FILE_END_craft_wondrous", 115); + SetPRCSwitch("PRC_FILE_END_craft_wondrous", 131); SetPRCSwitch("PRC_FILE_END_creaturesize", 5); SetPRCSwitch("PRC_FILE_END_creaturespeed", 8); SetPRCSwitch("PRC_FILE_END_crtemplates", 10); @@ -355,7 +356,7 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_des_crft_poison", 100); SetPRCSwitch("PRC_FILE_END_des_crft_props", 27); SetPRCSwitch("PRC_FILE_END_des_crft_scroll", 3999); - SetPRCSwitch("PRC_FILE_END_des_crft_spells", 19348); + SetPRCSwitch("PRC_FILE_END_des_crft_spells", 20000); SetPRCSwitch("PRC_FILE_END_des_crft_weapon", 29); SetPRCSwitch("PRC_FILE_END_des_cutconvdur", 26); SetPRCSwitch("PRC_FILE_END_des_feat2item", 1000); @@ -408,7 +409,7 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_iprp_ammocost", 15); SetPRCSwitch("PRC_FILE_END_iprp_ammotype", 2); SetPRCSwitch("PRC_FILE_END_iprp_amount", 4); - SetPRCSwitch("PRC_FILE_END_iprp_aoe", 7); + SetPRCSwitch("PRC_FILE_END_iprp_aoe", 8); SetPRCSwitch("PRC_FILE_END_iprp_arcspell", 19); SetPRCSwitch("PRC_FILE_END_iprp_base1", -1); SetPRCSwitch("PRC_FILE_END_iprp_bladecost", 5); @@ -423,15 +424,15 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_iprp_damvulcost", 7); SetPRCSwitch("PRC_FILE_END_iprp_decvalue1", 9); SetPRCSwitch("PRC_FILE_END_iprp_decvalue2", 9); - SetPRCSwitch("PRC_FILE_END_iprp_feats", 24819); + SetPRCSwitch("PRC_FILE_END_iprp_feats", 26999); SetPRCSwitch("PRC_FILE_END_iprp_immuncost", 7); SetPRCSwitch("PRC_FILE_END_iprp_immunity", 9); SetPRCSwitch("PRC_FILE_END_iprp_incvalue1", 9); SetPRCSwitch("PRC_FILE_END_iprp_incvalue2", 9); SetPRCSwitch("PRC_FILE_END_iprp_kitcost", 50); SetPRCSwitch("PRC_FILE_END_iprp_lightcost", 4); - SetPRCSwitch("PRC_FILE_END_iprp_matcost", 77); - SetPRCSwitch("PRC_FILE_END_iprp_material", 77); + SetPRCSwitch("PRC_FILE_END_iprp_matcost", 145); + SetPRCSwitch("PRC_FILE_END_iprp_material", 145); SetPRCSwitch("PRC_FILE_END_iprp_maxpp", 8); SetPRCSwitch("PRC_FILE_END_iprp_meleecost", 20); SetPRCSwitch("PRC_FILE_END_iprp_metamagic", 6); @@ -458,11 +459,11 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_iprp_soakcost", 50); SetPRCSwitch("PRC_FILE_END_iprp_speed_dec", 9); SetPRCSwitch("PRC_FILE_END_iprp_speed_enh", 9); - SetPRCSwitch("PRC_FILE_END_iprp_spellcost", 243); + SetPRCSwitch("PRC_FILE_END_iprp_spellcost", 298); SetPRCSwitch("PRC_FILE_END_iprp_spellcstr", 42); SetPRCSwitch("PRC_FILE_END_iprp_spelllvcost", 9); SetPRCSwitch("PRC_FILE_END_iprp_spelllvlimm", 9); - SetPRCSwitch("PRC_FILE_END_iprp_spells", 1456); + SetPRCSwitch("PRC_FILE_END_iprp_spells", 1552); SetPRCSwitch("PRC_FILE_END_iprp_spellshl", 7); SetPRCSwitch("PRC_FILE_END_iprp_srcost", 99); SetPRCSwitch("PRC_FILE_END_iprp_staminacost", -1); @@ -492,9 +493,9 @@ void SetDefaultFileEnds() SetPRCSwitch("PRC_FILE_END_itmwizwands", 38); SetPRCSwitch("PRC_FILE_END_keymap", 70); SetPRCSwitch("PRC_FILE_END_lightcolor", 32); - SetPRCSwitch("PRC_FILE_END_loadhints", 88); + SetPRCSwitch("PRC_FILE_END_loadhints", 101); SetPRCSwitch("PRC_FILE_END_loadscreens", 259); - SetPRCSwitch("PRC_FILE_END_masterfeats", 113); + SetPRCSwitch("PRC_FILE_END_masterfeats", 125); SetPRCSwitch("PRC_FILE_END_materialcomp", 200); SetPRCSwitch("PRC_FILE_END_metamagic", 6); SetPRCSwitch("PRC_FILE_END_namefilter", 3); @@ -866,6 +867,31 @@ void CreateSwitchNameArray() //if you add more switches, add them to this list array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DEBUG); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_COMBAT_DEBUG); + +//craft + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_CRAFT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MULTIPLIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MAX); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MIN); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BREW_POTION_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SCRIBE_SCROLL_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_WAND_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_ROD_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_STAFF_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_BASE_ITEMS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_MAXLEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_SCRIBESCROLL_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_MAXLEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CREATEINFUSION_COSTMODIFIER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_ARBITRARY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_COST_SCALE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_TIME_SCALE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_INFUSION_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_INFUSION_OPTIONAL_HERBS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_SCEPTER_CASTER_LEVEL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_MATERIAL_COMPONENTS); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_COMPONENTS_SHOP); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_TRUESEEING); @@ -876,6 +902,8 @@ void CreateSwitchNameArray() array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_HARM); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_NEUTRALIZE_POISON); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIOWARE_REMOVE_DISEASE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ALLOWED_TO_REMOVE_FRIENDLY_SPELLS); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_ALLOWED_TO_SEE_HOSTILE_SPELLS); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BIO_UNLEARN); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_UNLEARN_SPELL_MAXNR); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_TIMESTOP_BIOWARE_DURATION); @@ -994,7 +1022,7 @@ void CreateSwitchNameArray() array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_WEAPON_POISON_COUNT); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_USES_PER_WEAPON_POISON_DIE); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_POISON_ALLOW_CLEAN_IN_EQUIP); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_POISON_USE_INGREDIENST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_POISON_USE_INGREDIENTS); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PSI_ASTRAL_CONSTRUCT_USE_2DA); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_PNP_RAPID_METABOLISM); @@ -1058,31 +1086,31 @@ void CreateSwitchNameArray() array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_UNICORN_SQL); array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_LETOSCRIPT_GETNEWESTBIC); -//craft - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_DISABLE_CRAFT); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MULTIPLIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MAX); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_TIMER_MIN); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_BREW_POTION_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_SCRIBE_SCROLL_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_WAND_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_ROD_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFT_STAFF_CASTER_LEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_BASE_ITEMS); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_MAXLEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_BREWPOTION_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_SCRIBESCROLL_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_MAXLEVEL); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CRAFTWAND_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_X2_CREATEINFUSION_COSTMODIFIER); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_ARBITRARY); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_COST_SCALE); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CRAFTING_TIME_SCALE); - array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_CREATE_INFUSION_CASTER_LEVEL); - //spells //shifter + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPECHANGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_USECR); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_HUGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_LARGE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_MEDIUM); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_SMALL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_S_TINY); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_OUTSIDER); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ELEMENTAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_CONSTRUCT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_UNDEAD); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_DRAGON); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ABERRATION); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_OOZE); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_MAGICALBEAST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_GIANT); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_VERMIN); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_BEAST); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_ANIMAL); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_MONSTROUSHUMANOID); + array_set_string(oWP, "Switch_Name", array_get_size(oWP, "Switch_Name"), PNP_SHFT_F_HUMANOID); //general diff --git a/src/include/inv_inc_blast.nss b/src/include/inv_inc_blast.nss index 683b9cd..94709f6 100644 --- a/src/include/inv_inc_blast.nss +++ b/src/include/inv_inc_blast.nss @@ -1,4 +1,5 @@ #include "prc_inc_clsfunc" +#include "prc_inc_sp_tch" int GetBlastDamageDices(object oInvoker, int nInvokerLevel) { diff --git a/src/include/inv_inc_invknown.nss b/src/include/inv_inc_invknown.nss index dbd7acb..95800ad 100644 --- a/src/include/inv_inc_invknown.nss +++ b/src/include/inv_inc_invknown.nss @@ -135,6 +135,9 @@ int GetHasInvocation(int nInvocation, object oCreature = OBJECT_SELF); /* Includes */ ////////////////////////////////////////////////// +int GetPrimaryInvocationClass(object oCreature = OBJECT_SELF); +int GetInvocationPRCLevels(object oCaster); + #include "inc_item_props" #include "prc_x2_itemprop" #include "inc_lookups" diff --git a/src/include/inv_inc_invoke.nss b/src/include/inv_inc_invoke.nss index 0abd1e1..da67444 100644 --- a/src/include/inv_inc_invoke.nss +++ b/src/include/inv_inc_invoke.nss @@ -123,7 +123,7 @@ void DeleteLocalInvocation(object oObject, string sName); /* Includes */ ////////////////////////////////////////////////// -//#include "inv_inc_invfunc" //Access in parent +#include "inv_inc_invfunc" //Access in parent #include "prc_spellf_inc" ////////////////////////////////////////////////// diff --git a/src/include/inv_invokehook.nss b/src/include/inv_invokehook.nss index 92b0f1f..53010f3 100644 --- a/src/include/inv_invokehook.nss +++ b/src/include/inv_invokehook.nss @@ -77,6 +77,15 @@ int PreInvocationCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oInvoker); + //--------------------------------------------------------------------------- + // Block forsakers from using invocations + //--------------------------------------------------------------------------- + if(GetLevelByClass(CLASS_TYPE_FORSAKER, oInvoker) > 0) + { + SendMessageToPC(oInvoker, "Forsakers cannot use invocations."); + return FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- @@ -168,3 +177,4 @@ int PreInvocationCastCode() return nContinue; } +//:: void main (){} \ No newline at end of file diff --git a/src/include/moi_inc_moifunc.nss b/src/include/moi_inc_moifunc.nss index a2b0a3b..62af858 100644 --- a/src/include/moi_inc_moifunc.nss +++ b/src/include/moi_inc_moifunc.nss @@ -1170,7 +1170,10 @@ int GetMaxEssentiaCapacityFeat(object oMeldshaper) { int nMax = 1; // Always can invest one int nHD = GetHitDice(oMeldshaper); - if (nHD >= 31) nMax = 5; + if (nHD >= 61) nMax = 8; + else if (nHD >= 51) nMax = 7; + else if (nHD >= 41) nMax = 6; + else if (nHD >= 31) nMax = 5; else if (nHD >= 18) nMax = 4; else if (nHD >= 12) nMax = 3; else if (nHD >= 6) nMax = 2; diff --git a/src/include/pnp_shft_poly.nss b/src/include/pnp_shft_poly.nss index ce6d860..21b6663 100644 --- a/src/include/pnp_shft_poly.nss +++ b/src/include/pnp_shft_poly.nss @@ -10,9 +10,586 @@ void ShifterCheck(object oPC); #include "pnp_shft_main" #include "prc_inc_shifting" +//::////////////////Begin Werewolf////////////////// const string PRC_PNP_SHIFTING = "PRC_Shift"; -////////////////Begin Werewolf////////////////// +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon & Armor merge block + object oMergeWeaponSource = OBJECT_INVALID; + object oMergeArmorSource = OBJECT_INVALID; + + //:: Determine Weapon Merge Source + if (bWeapon) + { + if (bMonkGloves) + { + if (GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + else + { + //:: Always attempt to merge melee weapon to creature weapon + oMergeWeaponSource = oWeaponOld; // even if empty, ensures proper state + } + } + else + { + //:: Weapon not requested, but arms-slot allowed monk gloves can merge via armor branch + if (bMonkGloves && bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + + //:: Determine Armor Merge Source + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (!bMonkGloves) + { + if (bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeArmorSource = oGlovesOld; + + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + else + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: Apply Weapon Merge + if (GetIsObjectValid(oMergeWeaponSource) || bWeapon) + { + //:: Always attempt to merge weapon properties even if source is OBJECT_INVALID + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewBite, TRUE); + } + + //:: Apply Armor Merge + if (GetIsObjectValid(oMergeArmorSource)) + { + if (GetIsObjectValid(oArmorNew)) IPWildShapeCopyItemProperties(oMergeArmorSource, oArmorNew); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// + + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon & Armor merge block + object oMergeWeaponSource = OBJECT_INVALID; + object oMergeArmorSource = OBJECT_INVALID; + + // ---- Determine Weapon Merge Source ---- + if (bWeapon) + { + if (bMonkGloves) + { + if (GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + else + { + if (GetIsObjectValid(oWeaponOld)) + oMergeWeaponSource = oWeaponOld; + } + } + else + { + if (bMonkGloves && bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeWeaponSource = oGlovesOld; + } + + // ---- Determine Armor Merge Source ---- + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (!bMonkGloves) + { + if (bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + oMergeArmorSource = oGlovesOld; + + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + else + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + // ---- Apply Weapon Merge ---- + if (GetIsObjectValid(oMergeWeaponSource)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oMergeWeaponSource, oWeaponNewBite, TRUE); + } + + // ---- Apply Armor Merge ---- + if (GetIsObjectValid(oMergeArmorSource)) + { + if (GetIsObjectValid(oArmorNew)) IPWildShapeCopyItemProperties(oMergeArmorSource, oArmorNew); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// */ + + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon merge block + if (bWeapon) + { + object oMergeSource = OBJECT_INVALID; + + // Priority: monk gloves override if worn and arms-slot not allowed + if (bMonkGloves && !bArmsSlotAllowed) + { + if (GetIsObjectValid(oGlovesOld)) + oMergeSource = oGlovesOld; + } + else + { + // Otherwise use main-hand weapon if it exists + if (GetIsObjectValid(oWeaponOld)) + oMergeSource = oWeaponOld; + } + + // Apply merge to creature weapons if we have a source + if (GetIsObjectValid(oMergeSource)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oMergeSource, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oMergeSource, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oMergeSource, oWeaponNewBite, TRUE); + } + } + + //:: Armor merge block + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + + // Arms-slot allowed -> apply gloves & bracers to creature weapons + if (bArmsSlotAllowed && GetIsObjectValid(oGlovesOld)) + { + if (DEBUG) DoDebug("LycanthropePoly: Arms-slot allowed -> applying gloves/bracers to creature weapons from armor branch."); + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// + */ + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon merge block + //:: Only blocked if monk gloves are equipped AND arms-slot merge is NOT allowed + if (bWeapon && !bMonkGloves) + { + if (GetIsObjectValid(oWeaponOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewBite, TRUE); + } + } + else if (bWeapon && bMonkGloves && !bArmsSlotAllowed) + { + if (DEBUG) DoDebug("LycanthropePoly: Monk gloves overriding weapon merge (arms slot NOT allowed)."); + if (GetIsObjectValid(oGlovesOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + + + //:: Armor merge block + //:: Apply armor and gloves (if arms-slot allowed) + + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + + if (bArmsSlotAllowed && bMonkGloves && GetIsObjectValid(oGlovesOld)) + { + if (DEBUG) DoDebug("LycanthropePoly: Arms-slot allowed -> applying gloves to creature weapons from armor branch."); + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// */ + + +/* //::////////////////Begin Werewolf////////////////// +const string PRC_PNP_SHIFTING = "PRC_Shift"; + +void LycanthropePoly(object oPC, int nPoly) +{ + effect eVis = EffectVisualEffect(VFX_IMP_POLYMORPH); + effect ePoly = SupernaturalEffect(EffectPolymorph(nPoly)); + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + int bArmsSlotAllowed = GetPRCSwitch(PRC_WILDSHAPE_ALLOWS_ARMS_SLOT); + + int bWeapon = StringToInt(Get2DACache("polymorph","MergeW",nPoly)) == 1; + int bArmor = StringToInt(Get2DACache("polymorph","MergeA",nPoly)) == 1; + int bItems = StringToInt(Get2DACache("polymorph","MergeI",nPoly)) == 1; + + object oWeaponOld = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + object oArmorOld = GetItemInSlot(INVENTORY_SLOT_CHEST,oPC); + object oRing1Old = GetItemInSlot(INVENTORY_SLOT_LEFTRING,oPC); + object oRing2Old = GetItemInSlot(INVENTORY_SLOT_RIGHTRING,oPC); + object oAmuletOld = GetItemInSlot(INVENTORY_SLOT_NECK,oPC); + object oCloakOld = GetItemInSlot(INVENTORY_SLOT_CLOAK,oPC); + object oBootsOld = GetItemInSlot(INVENTORY_SLOT_BOOTS,oPC); + object oBeltOld = GetItemInSlot(INVENTORY_SLOT_BELT,oPC); + object oHelmetOld = GetItemInSlot(INVENTORY_SLOT_HEAD,oPC); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oPC); + object oGlovesOld = GetItemInSlot(INVENTORY_SLOT_ARMS,oPC); + + if (GetIsObjectValid(oShield)) + { + int nShieldType = GetBaseItemType(oShield); + if (nShieldType != BASE_ITEM_LARGESHIELD && + nShieldType != BASE_ITEM_SMALLSHIELD && + nShieldType != BASE_ITEM_TOWERSHIELD) + { + oShield = OBJECT_INVALID; + } + } + + ShifterCheck(oPC); + ClearAllActions(); + + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoly, oPC); + + object oWeaponNewRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oPC); + object oWeaponNewLeft = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L,oPC); + object oWeaponNewBite = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B,oPC); + object oArmorNew = GetItemInSlot(INVENTORY_SLOT_CARMOUR,oPC); + + //:: Weapon merge block + //:: Only blocked if monk gloves are equipped AND arms-slot merge is NOT allowed + if (bWeapon && !bMonkGloves) + { + if (GetIsObjectValid(oWeaponOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oWeaponOld, oWeaponNewBite, TRUE); + } + } + else if (bWeapon && bMonkGloves && !bArmsSlotAllowed) + { + if (DEBUG) DoDebug("LycanthropePoly: Monk gloves overriding weapon merge (arms slot NOT allowed)."); + if (GetIsObjectValid(oGlovesOld)) + { + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + + + //:: Armor merge block + //:: Apply armor and gloves (if arms-slot allowed) + + if (bArmor && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oShield)) IPWildShapeCopyItemProperties(oShield, oArmorNew); + if (GetIsObjectValid(oHelmetOld)) IPWildShapeCopyItemProperties(oHelmetOld, oArmorNew); + if (GetIsObjectValid(oArmorOld)) IPWildShapeCopyItemProperties(oArmorOld, oArmorNew); + + if (bArmsSlotAllowed && bMonkGloves && GetIsObjectValid(oGlovesOld)) + { + if (DEBUG) DoDebug("LycanthropePoly: Arms-slot allowed -> applying gloves to creature weapons from armor branch."); + if (GetIsObjectValid(oWeaponNewLeft)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewLeft, TRUE); + if (GetIsObjectValid(oWeaponNewRight)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewRight, TRUE); + if (GetIsObjectValid(oWeaponNewBite)) IPWildShapeCopyItemProperties(oGlovesOld, oWeaponNewBite, TRUE); + } + } + else if (bArmor && !GetIsObjectValid(oArmorNew) && DEBUG) + { + DoDebug("LycanthropePoly: MergeA set, but oArmorNew invalid."); + } + + //:: General item merge block + if (bItems && GetIsObjectValid(oArmorNew)) + { + if (GetIsObjectValid(oRing1Old)) IPWildShapeCopyItemProperties(oRing1Old, oArmorNew); + if (GetIsObjectValid(oRing2Old)) IPWildShapeCopyItemProperties(oRing2Old, oArmorNew); + if (GetIsObjectValid(oAmuletOld)) IPWildShapeCopyItemProperties(oAmuletOld, oArmorNew); + if (GetIsObjectValid(oCloakOld)) IPWildShapeCopyItemProperties(oCloakOld, oArmorNew); + if (GetIsObjectValid(oBootsOld)) IPWildShapeCopyItemProperties(oBootsOld, oArmorNew); + if (GetIsObjectValid(oBeltOld)) IPWildShapeCopyItemProperties(oBeltOld, oArmorNew); + } +} +//::////////////////End Werewolf////////////////// */ + + +/* ////////////////Begin Werewolf////////////////// void LycanthropePoly(object oPC, int nPoly) { @@ -84,7 +661,7 @@ void LycanthropePoly(object oPC, int nPoly) } -////////////////End Werewolf////////////////// +////////////////End Werewolf////////////////// */ void ShifterCheck(object oPC) { @@ -246,4 +823,6 @@ void DoTail(object oPC, int nTailType) SetCreatureTailType(nTailType, oPC); //override any stored default appearance SetPersistantLocalInt(oPC, "AppearanceStoredTail", nTailType); -} \ No newline at end of file +} + +//::void main (){} \ No newline at end of file diff --git a/src/include/prc_add_spl_pen.nss b/src/include/prc_add_spl_pen.nss index e339ebd..d7ca3d6 100644 --- a/src/include/prc_add_spl_pen.nss +++ b/src/include/prc_add_spl_pen.nss @@ -54,11 +54,11 @@ int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0); /* Includes */ ////////////////////////////////////////////////// -//#include "prc_inc_spells" +#include "prc_inc_spells" //#include "prc_alterations" //#include "prcsp_archmaginc" //#include "prc_inc_racial" - +#include "inc_2dacache" ////////////////////////////////////////////////// /* Function definitions */ diff --git a/src/include/prc_craft_inc.nss b/src/include/prc_craft_inc.nss index 8d0c398..ee87ad8 100644 --- a/src/include/prc_craft_inc.nss +++ b/src/include/prc_craft_inc.nss @@ -444,6 +444,7 @@ int Get2DALineFromItemprop(string sFile, itemproperty ip, object oItem) } break; } + case ITEM_PROPERTY_ECHOBLADE: return 46; break; } } return -1; @@ -1529,7 +1530,12 @@ void ApplyItemProps(object oItem, string sFile, int nLine) break; //no more itemprops, no gaps, assuming no errors } if(sFile != "craft_weapon" && sFile != "craft_armour") - SetName(oItem, GetStringByStrRef(StringToInt(Get2DACache(sFile, "Name", nLine)))); + { + SetName(oItem, GetStringByStrRef(StringToInt(Get2DACache(sFile, "Name", nLine)))); + string sDescRef = Get2DACache(sFile, "CraftedDescription", nLine); + if(sDescRef != "") + SetDescription(oItem, GetStringByStrRef(StringToInt(sDescRef))); + } } //Partly ripped off the lexicon :P @@ -1620,7 +1626,8 @@ string GetCrafting2DA(object oItem) (nBase == BASE_ITEM_BOOTS) || (nBase == BASE_ITEM_GLOVES) || (nBase == BASE_ITEM_BRACER) || - (nBase == BASE_ITEM_CLOAK)) + (nBase == BASE_ITEM_CLOAK) || + (nBase == BASE_ITEM_CRAFTED_VIAL)) ) return "craft_wondrous"; @@ -1657,19 +1664,28 @@ int GetCraftingFeat(object oItem) if(nBase == BASE_ITEM_RING) return FEAT_FORGE_RING; //routing bioware feats through this convo + if((nBase == BASE_ITEM_CRAFTED_SCEPTER) || + (nBase == BASE_ITEM_CRAFTED_SCEPTER) + ) + return FEAT_CRAFT_SCEPTER; + if((nBase == BASE_ITEM_MAGICROD) || (nBase == BASE_ITEM_CRAFTED_ROD) ) return FEAT_CRAFT_ROD; + if((nBase == BASE_ITEM_MAGICSTAFF) || (nBase == BASE_ITEM_CRAFTED_STAFF) ) return FEAT_CRAFT_STAFF; + if((nBase == BASE_ITEM_MAGICWAND) || (nBase == BASE_ITEM_BLANK_WAND) ) return FEAT_CRAFT_WAND; + if(nBase == BASE_ITEM_BLANK_POTION) return FEAT_BREW_POTION; + if(nBase == BASE_ITEM_BLANK_SCROLL) return FEAT_SCRIBE_SCROLL; if(((nBase == BASE_ITEM_HELMET) || @@ -1678,7 +1694,8 @@ int GetCraftingFeat(object oItem) (nBase == BASE_ITEM_BOOTS) || (nBase == BASE_ITEM_GLOVES) || (nBase == BASE_ITEM_BRACER) || - (nBase == BASE_ITEM_CLOAK)) + (nBase == BASE_ITEM_CLOAK) || + (nBase == BASE_ITEM_CRAFTED_VIAL)) ) return FEAT_CRAFT_WONDROUS; diff --git a/src/include/prc_effect_inc.nss b/src/include/prc_effect_inc.nss index 4552481..a41f7d9 100644 --- a/src/include/prc_effect_inc.nss +++ b/src/include/prc_effect_inc.nss @@ -75,10 +75,11 @@ void DeathlessFrenzyCheck(object oTarget); // * PRC Version of a Bioware function to disable include loops void PRCRemoveSpellEffects(int nSpell_ID, object oCaster, object oTarget); + /** * Target is immune to gaze attacks * - * @return the Dazzle effect + * @return the Gaze Immunity effect */ effect EffectGazeImmune(); @@ -89,6 +90,9 @@ effect EffectGazeImmune(); */ effect EffectDazzle(); +//ebonfowl: adding this function to check if a target is already shaken +int GetIsShaken(object oTarget); + /** * Shaken effect: -2 to attack, all skills and saving throws * @@ -177,14 +181,11 @@ effect EffectAbilityBasedSkillIncrease(int iAbility, int iIncrease = 1); */ effect EffectAbilityBasedSkillDecrease(int iAbility, int iDecrease = 1); -//ebonfowl: adding this function to check if a target is already shaken -int GetIsShaken(object oTarget); - ////////////////////////////////////////////////// /* Include section */ ////////////////////////////////////////////////// - #include "prc_inc_castlvl" // get prc_racial_const, prc_inc_nwscript, prc_inc_newip +#include "inc_epicspelldef" ////////////////////////////////////////////////// /* Internal functions */ @@ -268,6 +269,8 @@ object GetObjectToApplyNewEffect(string sTag, object oPC, int nStripEffects = TR SetCreatureAppearanceType(oWP, APPEARANCE_TYPE_INVISIBLE_HUMAN_MALE); ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY), oWP); ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneGhost(), oWP); + AssignCommand(oWP, ActionUseSkill(SKILL_HIDE, oWP)); + } //remove previous effects if(nStripEffects) @@ -746,7 +749,6 @@ effect EffectImmunityMiscAll() //:: Immunity to all gaze attacks effect EffectGazeImmune() { - effect eBlank; effect eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_CHARM); eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_CONFUSION); @@ -768,6 +770,23 @@ effect EffectGazeImmune() return eReturn; } +//:: Immunity to all perification attacks +effect EffectPetrificationImmune() +{ + effect eReturn = EffectSpellImmunity(SPELLABILITY_TOUCH_PETRIFY); + eReturn = EffectSpellImmunity(SPELLABILITY_BREATH_PETRIFY); + eReturn = EffectSpellImmunity(SPELL_FLESH_TO_STONE); + eReturn = EffectSpellImmunity(SPELL_STONEHOLD); + eReturn = EffectSpellImmunity(SPELL_EPIC_A_STONE); + eReturn = EffectSpellImmunity(POWER_CRYSTALLIZE); + eReturn = EffectSpellImmunity(MELD_BASILISK_MASK); + eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_PETRIFY); + + eReturn = TagEffect(eReturn, "PRCPetrificationImmune"); + + return eReturn; +} + int GetIsShaken(object oTarget) { effect eEffect = GetFirstEffect(oTarget); @@ -785,5 +804,11 @@ int GetIsShaken(object oTarget) return FALSE; } +// Forward declarations for size change effects +// Implementations are in prc_inc_size +effect EffectSizeChange(object oTarget, int nObjectType, int bEnlarge, int nChanges); +void DelayedSetVisualTransform(int nExpectedGeneration, object oTarget, int nTransform, float fValue); +void DelaySetVisualTransform(float fDelay, object oTarget, string sGenerationName, int nTransform, float fValue); + //:: Test void -//:: void main() {} \ No newline at end of file +//::void main() {} \ No newline at end of file diff --git a/src/include/prc_feat_const.nss b/src/include/prc_feat_const.nss index 8035960..6fd8c94 100644 --- a/src/include/prc_feat_const.nss +++ b/src/include/prc_feat_const.nss @@ -4,6 +4,11 @@ //:: PRC Options Conversation const int FEAT_OPTIONS_CONVERSATION = 2285; +//;; Builder Feats +const int FEAT_ARCHETYPAL_FORM = 2918; +const int FEAT_INTRINSIC_ARMOR = 25990; +const int FEAT_INTRINSIC_WEAPON = 25991; + //:: Missing Bioware Feats const int FEAT_EPIC_PLANAR_TURNING = 854; @@ -219,6 +224,9 @@ const int FEAT_MAGICAL_ARTISAN_CREATE_INFUSION = 25961; const int FEAT_PLANT_DEFIANCE = 25992; const int FEAT_PLANT_CONTROL = 25993; + //:: Lost Empires of Faerun feats +const int FEAT_CRAFT_SCEPTER = 25962; +const int FEAT_MAGICAL_ARTISAN_CRAFT_SCEPTER = 25963; //:: Racial Feats const int FEAT_WEMIC_JUMP_8 = 4518; @@ -1314,6 +1322,7 @@ const int FEAT_SOMATIC_WEAPONRY = 5186; // Forgotten Realms Campaign Setting const int FEAT_INSCRIBE_RUNE = 2462; +const int EPIC_FEAT_INSCRIBE_EPIC_RUNES = 2549; // Miniature Handbook const int FEAT_SHIELDMATE = 3258; @@ -3726,6 +3735,9 @@ const int FEAT_EPIC_ARTIFICER = 4072; //////////////// END INFUSIONS ///////////////// //////////////////////////////////////////////////*/ +//:: Monk +const int FEAT_MONK_ABUNDANT_STEP = 2351; + //Justice of Weald and Woe const int FEAT_LUCKY_SHOT = 24021; @@ -6275,6 +6287,7 @@ const int FEAT_DSONG_SPELLCASTING_NONE = 19592; const int FEAT_OLLAM_SPELLCASTING_NONE = 19593; //:: PRC8 Hidden Talent Feats +const int FEAT_HIDDEN_TALENT = 25900; const int FEAT_HIDDEN_TALENT_BIOFEEDBACK = 25901; const int FEAT_HIDDEN_TALENT_BITE_WOLF = 25902; const int FEAT_HIDDEN_TALENT_BOLT = 25903; diff --git a/src/include/prc_getbest_inc.nss b/src/include/prc_getbest_inc.nss index a17eb0a..a0efeca 100644 --- a/src/include/prc_getbest_inc.nss +++ b/src/include/prc_getbest_inc.nss @@ -400,5 +400,4 @@ int GetBestAvailableSpell(object oTarget) if(nBestSpell == 99999) nBestSpell = GetBestL1Spell(oTarget, nBestSpell); if(nBestSpell == 99999) nBestSpell = GetBestL0Spell(oTarget, nBestSpell); return nBestSpell; -} - +} \ No newline at end of file diff --git a/src/include/prc_inc_breath.nss b/src/include/prc_inc_breath.nss index 0d439f9..094029c 100644 --- a/src/include/prc_inc_breath.nss +++ b/src/include/prc_inc_breath.nss @@ -124,6 +124,8 @@ void ApplyBreath(struct breath BreathUsed, location lTargetArea, int bLinger = F ////////////////////////////////////////////////// #include "prc_alterations" +#include "prcsp_archmaginc" +#include "prc_inc_spells" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/prc_inc_castlvl.nss b/src/include/prc_inc_castlvl.nss index 8336a62..0c1a8be 100644 --- a/src/include/prc_inc_castlvl.nss +++ b/src/include/prc_inc_castlvl.nss @@ -3859,7 +3859,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_ARCHIVIST, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_ARCHIVIST, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BFZ_SPELLCASTING_ARCHIVIST, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; @@ -4193,7 +4196,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_CLERIC, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_CLERIC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BFZ_SPELLCASTING_CLERIC, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; @@ -4309,9 +4315,12 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); /* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_DRUID, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); */ + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_DRUID, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); - if(GetHasFeat(FEAT_BFZ_SPELLCASTING_DRUID, oCaster)) +/* if(GetHasFeat(FEAT_BFZ_SPELLCASTING_DRUID, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_BFZ, oCaster + 1) / 2 */ // if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_DRUID, oCaster)) @@ -4421,7 +4430,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_FAVOURED_SOUL, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_FAVOURED_SOUL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BFZ_SPELLCASTING_FAVOURED_SOUL, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; @@ -4534,7 +4546,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); */ /* if(GetHasFeat(FEAT_BFZ_SPELLCASTING_HEALER, oCaster)) - nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; */ + nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; */ + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_HEALER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BRIMSTONE_SPEAKER_SPELLCASTING_HEALER, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oCaster) + 1) / 2; @@ -4641,7 +4656,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_JUSTICEWW, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_JOWAW, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BFZ_SPELLCASTING_JUSTICEWW, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; @@ -4742,6 +4760,9 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_KNIGHT_CHALICE, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_KOTC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); /* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_KNIGHT_CHALICE, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); @@ -4849,6 +4870,9 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_KOTMC, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); /* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_KNIGHT_MIDDLECIRCLE, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); @@ -4962,6 +4986,9 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_NENTYAR_HUNTER, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_NENTYAR_HUNTER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); /* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_NENTYAR_HUNTER, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); */ @@ -5164,6 +5191,9 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_PALADIN, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_PALADIN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); /* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_PALADIN, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); @@ -5272,7 +5302,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_RANGER, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_RANGER, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BFZ_SPELLCASTING_RANGER, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; @@ -5385,7 +5418,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_OASHAMAN, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_OASHAMAN, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BFZ_SPELLCASTING_OASHAMAN, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; @@ -5599,7 +5635,10 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_SOHEI, oCaster)) - nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_SOHEI, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); if(GetHasFeat(FEAT_BFZ_SPELLCASTING_SOHEI, oCaster)) nDivine += (GetLevelByClass(CLASS_TYPE_BFZ, oCaster) + 1) / 2; @@ -5704,6 +5743,9 @@ int GetDivinePRCLevels(object oCaster, int nCastingClass = CLASS_TYPE_INVALID) if(GetHasFeat(FEAT_SWIFT_WING_SPELLCASTING_SOL, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_SWIFT_WING, oCaster); + + if(GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_SOL, oCaster)) + nDivine += GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oCaster); /* if(GetHasFeat(FEAT_TENEBROUS_APOSTATE_SPELLCASTING_SOL, oCaster)) nDivine += GetLevelByClass(CLASS_TYPE_TENEBROUS_APOSTATE, oCaster); diff --git a/src/include/prc_inc_chat_pow.nss b/src/include/prc_inc_chat_pow.nss index aced79a..f1c1b64 100644 --- a/src/include/prc_inc_chat_pow.nss +++ b/src/include/prc_inc_chat_pow.nss @@ -16,6 +16,7 @@ Command summary: */ #include "prc_inc_chat" +#include "inc_persist_loca" const string CMD_POWER_ATTACK = "pow-erattack"; diff --git a/src/include/prc_inc_clsfunc.nss b/src/include/prc_inc_clsfunc.nss index 4d1ffcb..9bd42ed 100644 --- a/src/include/prc_inc_clsfunc.nss +++ b/src/include/prc_inc_clsfunc.nss @@ -380,6 +380,7 @@ int Vile_Feat(int iTypeWeap) case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_VILE_MARTIAL_BASTARDSWORD); case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_VILE_MARTIAL_BATTLEAXE); case BASE_ITEM_CLUB: return GetHasFeat(FEAT_VILE_MARTIAL_CLUB); + case BASE_ITEM_CRAFTED_SCEPTER: return GetHasFeat(FEAT_VILE_MARTIAL_CLUB); case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_VILE_MARTIAL_DAGGER); case BASE_ITEM_DART: return GetHasFeat(FEAT_VILE_MARTIAL_DART); case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_VILE_MARTIAL_DIREMACE); @@ -426,7 +427,7 @@ int Vile_Feat(int iTypeWeap) GetHasFeat(FEAT_VILE_MARTIAL_RAPIER) || GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_THINBLADE)); - case BASE_ITEM_ELVEN_COURTBLADE: return GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD || + case BASE_ITEM_ELVEN_COURTBLADE: return (GetHasFeat(FEAT_VILE_MARTIAL_GREATSWORD) || GetHasFeat(FEAT_VILE_MARTIAL_ELVEN_COURTBLADE)); case BASE_ITEM_DOUBLE_SCIMITAR: return GetHasFeat(FEAT_VILE_MARTIAL_DBL_SCIMITAR); @@ -461,6 +462,7 @@ int GetSanctifedMartialFeat(int iTypeWeap) case BASE_ITEM_BASTARDSWORD: return FEAT_SANCTIFY_MARTIAL_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_SANCTIFY_MARTIAL_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_SANCTIFY_MARTIAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_SANCTIFY_MARTIAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_SANCTIFY_MARTIAL_DAGGER; case BASE_ITEM_DART: return FEAT_SANCTIFY_MARTIAL_DART; case BASE_ITEM_DIREMACE: return FEAT_SANCTIFY_MARTIAL_DIREMACE; @@ -535,6 +537,7 @@ int Sanctify_Feat(int iTypeWeap) case BASE_ITEM_BASTARDSWORD: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BASTARDSWORD); case BASE_ITEM_BATTLEAXE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_BATTLEAXE); case BASE_ITEM_CLUB: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_CLUB); + case BASE_ITEM_CRAFTED_SCEPTER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_CLUB); case BASE_ITEM_DAGGER: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DAGGER); case BASE_ITEM_DART: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DART); case BASE_ITEM_DIREMACE: return GetHasFeat(FEAT_SANCTIFY_MARTIAL_DIREMACE); diff --git a/src/include/prc_inc_combat.nss b/src/include/prc_inc_combat.nss index df4a5b7..6b143b5 100644 --- a/src/include/prc_inc_combat.nss +++ b/src/include/prc_inc_combat.nss @@ -1082,7 +1082,7 @@ int GetIsTwoHandedMeleeWeaponType(int iWeaponType) case BASE_ITEM_HEAVYFLAIL: return TRUE; case BASE_ITEM_SCYTHE: return TRUE; case BASE_ITEM_QUARTERSTAFF: return TRUE; - case BASE_ITEM_MAGICSTAFF: return TRUE; + //case BASE_ITEM_MAGICSTAFF: return TRUE; case BASE_ITEM_ELVEN_COURTBLADE: return TRUE; case BASE_ITEM_MAUL: return TRUE; case BASE_ITEM_FALCHION: return TRUE; @@ -1141,8 +1141,8 @@ int GetIsSimpleWeaponType(int iWeaponType) case BASE_ITEM_CSLSHPRCWEAP: return 1; case BASE_ITEM_GLOVES: return 1; case BASE_ITEM_BRACER: return 1; - - case BASE_ITEM_CLUB: return 2; + case BASE_ITEM_CRAFTED_SCEPTER: return 1; + case BASE_ITEM_DAGGER: return 2; case BASE_ITEM_LIGHTMACE: return 2; case BASE_ITEM_SICKLE: return 2; @@ -1279,6 +1279,19 @@ struct WeaponFeat GetAllFeatsOfWeaponType(int iWeaponType) sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_CLUB; break; } + case BASE_ITEM_CRAFTED_SCEPTER: { + sFeat.Focus = FEAT_WEAPON_FOCUS_CLUB; + sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_CLUB; + sFeat.EpicFocus = FEAT_EPIC_WEAPON_FOCUS_CLUB; + sFeat.EpicSpecialization = FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; + sFeat.ImprovedCritical = FEAT_IMPROVED_CRITICAL_CLUB; + sFeat.OverwhelmingCritical = FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; + sFeat.DevastatingCritical = FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; + sFeat.WeaponOfChoice = FEAT_WEAPON_OF_CHOICE_CLUB; + sFeat.SanctifyMartialStrike = FEAT_SANCTIFY_MARTIAL_CLUB; + sFeat.VileMartialStrike = FEAT_VILE_MARTIAL_CLUB; + break; + } case BASE_ITEM_DAGGER: { sFeat.Focus = FEAT_WEAPON_FOCUS_DAGGER; sFeat.Specialization = FEAT_WEAPON_SPECIALIZATION_DAGGER; @@ -8090,7 +8103,12 @@ void AttackLoopLogic(object oDefender, object oAttacker, if (DEBUG) DoDebug("entered AttackLoopLogic: bFirstAttack = " + IntToString(bFirstAttack) + ", cleave = " + IntToString(bIsCleaveAttack) + ", current action = " + GetActionName(iAction)); if (DEBUG) DoDebug("AttackLoopLogic: iMainAttacks = " + IntToString(iMainAttacks) + ", iOffHandAttacks = " + IntToString(iOffHandAttacks) + ", iBonusAttacks = " + IntToString(iBonusAttacks)); - int bIsRangedAttack = sAttackVars.bIsRangedWeapon || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED; + //int bIsRangedAttack = sAttackVars.bIsRangedWeapon || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED; + + int bIsRangedAttack = sAttackVars.bIsRangedWeapon || + sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED_SPELL || + sAttackVars.iTouchAttackType == TOUCH_ATTACK_RANGED || + GetLocalInt(oAttacker, "WhirlingBlade"); // check for valid target etc., but only if it is not a cleave or circle kick (in this case we checked all of this before) if (!bIsCleaveAttack) diff --git a/src/include/prc_inc_combmove.nss b/src/include/prc_inc_combmove.nss index d280dd1..180925b 100644 --- a/src/include/prc_inc_combmove.nss +++ b/src/include/prc_inc_combmove.nss @@ -273,6 +273,7 @@ void TigerBlooded(object oInitiator, object oTarget); #include "prc_inc_combat" #include "prc_inc_sp_tch" +#include "prc_feat_const" ////////////////////////////////////////////////// /* Internal functions */ @@ -1321,7 +1322,29 @@ int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, DelayCommand(0.0, PerformAttack(oTarget, oPC, eNone, 0.0, 0, 0, 0, "Improved Trip Free Attack Hit", "Improved Trip Free Attack Miss")); } } - else // If you fail, enemy gets a counter trip attempt, using Strength + else // If you fail, enemy gets a counter trip attempt, using Strength + { + if(!nCounterTrip) + { + nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, FALSE, TRUE); + FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); + // Roll counter trip attempt + nTargetCheck = nTargetStat + nTargetBonus + d20(); + nPCCheck = nPCStat + nPCBonus + d20(); + // If counters aren't allowed, don't knock em down + // Its down here to allow the text message to go through + SendMessageToPC(oPC, "Enemy Counter Trip Check: "+IntToString(nPCCheck)+" vs "+IntToString(nTargetCheck)); + + SetLocalInt(oPC, "TripDifference", nTargetCheck - nPCCheck); + DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference")); + } + if (nTargetCheck >= nPCCheck && nCounterTrip) + { + // Knock em down + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(EffectKnockdown()), oPC, 6.0); + } + } +/* else // If you fail, enemy gets a counter trip attempt, using Strength { nTargetStat = GetAbilityModifier(ABILITY_STRENGTH, oTarget) + GetCombatMoveCheckBonus(oTarget, COMBAT_MOVE_TRIP, FALSE, TRUE); FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); @@ -1338,7 +1361,7 @@ int DoTrip(object oPC, object oTarget, int nExtraBonus, int nGenerateAoO = TRUE, } SetLocalInt(oPC, "TripDifference", nTargetCheck - nPCCheck); DelayCommand(2.0, DeleteLocalInt(oPC, "TripDifference")); - } + } */ } else FloatingTextStringOnCreature("You have failed on your Trip attempt",oPC, FALSE); @@ -1941,10 +1964,21 @@ void TigerBlooded(object oInitiator, object oTarget) int DoDisarm(object oPC, object oTarget, int nExtraBonus = 0, int nGenerateAoO = TRUE, int nCounter = TRUE) { object oTargetWep = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + + int bNoDisarm = GetHasFeat(FEAT_INTRINSIC_WEAPON, oTarget); + + string sName = GetName(oTarget); + + if(bNoDisarm) + { + FloatingTextStringOnCreature(sName+" is wielding an intrinsic weapon", oPC, FALSE); + AssignCommand(oPC, ActionAttack(oTarget)); + return FALSE; + } if (!GetIsObjectValid(oTargetWep) || GetPlotFlag(oTargetWep) || (!GetIsCreatureDisarmable(oTarget) && !GetPRCSwitch(PRC_PNP_DISARM)) || GetLocalInt(oTarget, "TigerFangDisarm")) { - FloatingTextStringOnCreature("Target is not a legal target", oPC, FALSE); + FloatingTextStringOnCreature(sName+" is not a legal target", oPC, FALSE); AssignCommand(oPC, ActionAttack(oTarget)); return FALSE; } diff --git a/src/include/prc_inc_core.nss b/src/include/prc_inc_core.nss index 099c3ac..df73451 100644 --- a/src/include/prc_inc_core.nss +++ b/src/include/prc_inc_core.nss @@ -133,6 +133,7 @@ const int METAMAGIC_QUICKEN_LEVEL = 4; #include "prc_inc_damage" #include "prc_inc_sb_const" // Spell Book Constants #include "x0_i0_position" +#include "inc_newspellbook" /* access to prc_inc_nwscript via prc_inc_damage @@ -410,6 +411,8 @@ int PRCGetSpellLevelForClass(int nSpell, int nClass) sSpellLevel = Get2DACache("spells", "Cleric", nSpell); else if (nClass == CLASS_TYPE_BARD) sSpellLevel = Get2DACache("spells", "Bard", nSpell); + else if (nClass == CLASS_TYPE_ASSASSIN) + sSpellLevel = Get2DACache("spells", "Assassin", nSpell); else if (nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK) sSpellLevel = Get2DACache("spells", "Cultist", nSpell); else if (nClass == CLASS_TYPE_NENTYAR_HUNTER) diff --git a/src/include/prc_inc_descrptr.nss b/src/include/prc_inc_descrptr.nss index bbee255..d814a6c 100644 --- a/src/include/prc_inc_descrptr.nss +++ b/src/include/prc_inc_descrptr.nss @@ -169,8 +169,8 @@ int GetSubschoolFlags(int nSpellID); /* Includes */ ////////////////////////////////////////////////// -#include "inc_2dacache" // already has access via inc_utility -//#include "inc_utility" +#include "inc_2dacache" +#include "inc_utility" ////////////////////////////////////////////////// /* Function definitions */ diff --git a/src/include/prc_inc_factotum.nss b/src/include/prc_inc_factotum.nss index 46be9c4..eee86b1 100644 --- a/src/include/prc_inc_factotum.nss +++ b/src/include/prc_inc_factotum.nss @@ -49,6 +49,8 @@ const int BRILLIANCE_SLOT_3 = 3919; ////////////////////////////////////////////////// /* Function definitions */ ////////////////////////////////////////////////// +void TriggerInspiration(object oPC, int nCombat); + void PrepareArcDilSpell(object oPC, int nSpell) { @@ -188,7 +190,8 @@ void SetInspiration(object oPC) for(i = FEAT_FONT_INSPIRATION_1; i <= FEAT_FONT_INSPIRATION_10; i++) if(GetHasFeat(i, oPC)) nFont++; - nInspiration += nFont * (1 + nFont + 1) / 2; + //nInspiration += nFont * (1 + nFont + 1) / 2; + nInspiration += nFont * (nFont + 1) / 2; SetLocalInt(oPC, "InspirationPool", nInspiration); FloatingTextStringOnCreature("Encounter begins with "+IntToString(nInspiration)+" inspiration", oPC, FALSE); } @@ -201,6 +204,8 @@ void ClearInspiration(object oPC) int ExpendInspiration(object oPC, int nCost) { + if (nCost <= 0) return FALSE; + int nInspiration = GetLocalInt(oPC, "InspirationPool"); if (nInspiration >= nCost) { @@ -261,6 +266,21 @@ void FactotumTriggerAbil(object oPC, int nAbil) IPSafeAddItemProperty(oSkin, ipIP, 60.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE); } +void TriggerInspiration(object oPC, int nCombat) +{ + SetLocalInt(oPC, "InspirationHBRunning", TRUE); + DelayCommand(0.249, DeleteLocalInt(oPC, "InspirationHBRunning")); + int nCurrent = GetIsInCombat(oPC); + // We just entered combat + if (nCurrent == TRUE && nCombat == FALSE) + SetInspiration(oPC); + else if (nCurrent == FALSE && nCombat == TRUE) // Just left combat + ClearInspiration(oPC); + + DelayCommand(0.25, TriggerInspiration(oPC, nCurrent)); +} + + /*void AddCunningBrillianceAbility(object oPC, int nAbil) { if (DEBUG) DoDebug("AddCunningBrillianceAbility "+IntToString(nAbil)); diff --git a/src/include/prc_inc_fork.nss b/src/include/prc_inc_fork.nss index 2b19f97..3b82eeb 100644 --- a/src/include/prc_inc_fork.nss +++ b/src/include/prc_inc_fork.nss @@ -23,11 +23,14 @@ const int FEAT_TYPE_IMPROVED_CRITICAL = 5; const int FEAT_TYPE_OVERWHELMING_CRITICAL = 6; const int FEAT_TYPE_DEVASTATING_CRITICAL = 7; const int FEAT_TYPE_WEAPON_OF_CHOICE = 8; +const int FEAT_TYPE_WEAPON_PROFICIENCY = 9; ////////////////////////////////////////////////// /* Function prototypes */ ////////////////////////////////////////////////// + int GetProficiencyFeatOfWeaponType(int iWeaponType); + /** * Returns the appropriate weapon feat given a weapon type. * @@ -210,10 +213,86 @@ int GetFeatOfWeaponType(int iWeaponType, int iFeatType) case FEAT_TYPE_OVERWHELMING_CRITICAL: return GetOverwhelmingCriticalFeatOfWeaponType(iWeaponType); case FEAT_TYPE_DEVASTATING_CRITICAL: return GetDevastatingCriticalFeatOfWeaponType(iWeaponType); case FEAT_TYPE_WEAPON_OF_CHOICE: return GetWeaponOfChoiceFeatOfWeaponType(iWeaponType); + case FEAT_TYPE_WEAPON_PROFICIENCY: return GetProficiencyFeatOfWeaponType(iWeaponType); } return -1; } +int GetProficiencyFeatOfWeaponType(int iWeaponType) +{ + switch(iWeaponType) + { + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: return FEAT_WEAPON_PROFICIENCY_CREATURE; + case BASE_ITEM_INVALID: return FEAT_IMPROVED_UNARMED_STRIKE; + + case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD; + case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_PROFICIENCY_BATTLEAXE; + case BASE_ITEM_CLUB: return FEAT_WEAPON_PROFICIENCY_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_PROFICIENCY_CLUB; + case BASE_ITEM_DAGGER: return FEAT_WEAPON_PROFICIENCY_DAGGER; + case BASE_ITEM_DART: return FEAT_WEAPON_PROFICIENCY_DART; + case BASE_ITEM_DIREMACE: return FEAT_WEAPON_PROFICIENCY_DIRE_MACE; + case BASE_ITEM_DOUBLEAXE: return FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE; + case BASE_ITEM_DWARVENWARAXE: return FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE; + case BASE_ITEM_GREATAXE: return FEAT_WEAPON_PROFICIENCY_GREATAXE; + case BASE_ITEM_GREATSWORD: return FEAT_WEAPON_PROFICIENCY_GREATSWORD; + case BASE_ITEM_HALBERD: return FEAT_WEAPON_PROFICIENCY_HALBERD; + case BASE_ITEM_HANDAXE: return FEAT_WEAPON_PROFICIENCY_HANDAXE; + case BASE_ITEM_HEAVYCROSSBOW: return FEAT_WEAPON_PROFICIENCY_HEAVY_XBOW; + case BASE_ITEM_HEAVYFLAIL: return FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL; + case BASE_ITEM_KAMA: return FEAT_WEAPON_PROFICIENCY_KAMA; + case BASE_ITEM_KATANA: return FEAT_WEAPON_PROFICIENCY_KATANA; + case BASE_ITEM_KUKRI: return FEAT_WEAPON_PROFICIENCY_KUKRI; + case BASE_ITEM_LIGHTCROSSBOW: return FEAT_WEAPON_PROFICIENCY_LIGHT_XBOW; + case BASE_ITEM_LIGHTFLAIL: return FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL; + case BASE_ITEM_LIGHTHAMMER: return FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER; + case BASE_ITEM_LIGHTMACE: return FEAT_WEAPON_PROFICIENCY_LIGHT_MACE; + case BASE_ITEM_LONGBOW: return FEAT_WEAPON_PROFICIENCY_LONGBOW; + case BASE_ITEM_LONGSWORD: return FEAT_WEAPON_PROFICIENCY_LONGSWORD; + case BASE_ITEM_MORNINGSTAR: return FEAT_WEAPON_PROFICIENCY_MORNINGSTAR; + case BASE_ITEM_QUARTERSTAFF: return FEAT_WEAPON_PROFICIENCY_QUARTERSTAFF; + case BASE_ITEM_MAGICSTAFF: return FEAT_WEAPON_PROFICIENCY_QUARTERSTAFF; + case BASE_ITEM_RAPIER: return FEAT_WEAPON_PROFICIENCY_RAPIER; + case BASE_ITEM_SCIMITAR: return FEAT_WEAPON_PROFICIENCY_SCIMITAR; + case BASE_ITEM_SCYTHE: return FEAT_WEAPON_PROFICIENCY_SCYTHE; + case BASE_ITEM_SHORTBOW: return FEAT_WEAPON_PROFICIENCY_SHORTBOW; + case BASE_ITEM_SHORTSPEAR: return FEAT_WEAPON_PROFICIENCY_SHORTSPEAR; + case BASE_ITEM_SHORTSWORD: return FEAT_WEAPON_PROFICIENCY_SHORTSWORD; + case BASE_ITEM_SHURIKEN: return FEAT_WEAPON_PROFICIENCY_SHURIKEN; + case BASE_ITEM_SICKLE: return FEAT_WEAPON_PROFICIENCY_SICKLE; + case BASE_ITEM_SLING: return FEAT_WEAPON_PROFICIENCY_SLING; + case BASE_ITEM_THROWINGAXE: return FEAT_WEAPON_PROFICIENCY_THROWING_AXE; + case BASE_ITEM_TRIDENT: return FEAT_WEAPON_PROFICIENCY_TRIDENT; + case BASE_ITEM_TWOBLADEDSWORD: return FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD; + case BASE_ITEM_WARHAMMER: return FEAT_WEAPON_PROFICIENCY_WARHAMMER; + case BASE_ITEM_WHIP: return FEAT_WEAPON_PROFICIENCY_WHIP; + + //:: new item types + case BASE_ITEM_DOUBLE_SCIMITAR: return FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR; + case BASE_ITEM_EAGLE_CLAW: return FEAT_WEAPON_PROFICIENCY_EAGLE_CLAW; + case BASE_ITEM_ELVEN_COURTBLADE: return FEAT_WEAPON_PROFICIENCY_ELVEN_COURTBLADE; + case BASE_ITEM_ELVEN_LIGHTBLADE: return FEAT_WEAPON_PROFICIENCY_ELVEN_LIGHTBLADE; + case BASE_ITEM_ELVEN_THINBLADE: return FEAT_WEAPON_PROFICIENCY_ELVEN_THINBLADE; + case BASE_ITEM_FALCHION: return FEAT_WEAPON_PROFICIENCY_FALCHION; + case BASE_ITEM_GOAD: return FEAT_WEAPON_PROFICIENCY_GOAD; + case BASE_ITEM_HEAVY_MACE: return FEAT_WEAPON_PROFICIENCY_HEAVY_MACE; + case BASE_ITEM_HEAVY_PICK: return FEAT_WEAPON_PROFICIENCY_HEAVY_PICK; + case BASE_ITEM_KATAR: return FEAT_WEAPON_PROFICIENCY_KATAR; + case BASE_ITEM_LIGHT_LANCE: return FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE; + case BASE_ITEM_LIGHT_PICK: return FEAT_WEAPON_PROFICIENCY_LIGHT_PICK; + case BASE_ITEM_MAUL: return FEAT_WEAPON_PROFICIENCY_MAUL; + case BASE_ITEM_NUNCHAKU: return FEAT_WEAPON_PROFICIENCY_NUNCHAKU; + case BASE_ITEM_SAI: return FEAT_WEAPON_PROFICIENCY_SAI; + case BASE_ITEM_SAP: return FEAT_WEAPON_PROFICIENCY_SAP; + } + + return -1; +} + + int GetFocusFeatOfWeaponType(int iWeaponType) { switch(iWeaponType) @@ -226,6 +305,7 @@ int GetFocusFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_FOCUS_BASTARD_SWORD; case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_FOCUS_BATTLE_AXE; case BASE_ITEM_CLUB: return FEAT_WEAPON_FOCUS_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_FOCUS_CLUB; case BASE_ITEM_DAGGER: return FEAT_WEAPON_FOCUS_DAGGER; case BASE_ITEM_DART: return FEAT_WEAPON_FOCUS_DART; case BASE_ITEM_DIREMACE: return FEAT_WEAPON_FOCUS_DIRE_MACE; @@ -297,6 +377,7 @@ int GetSpecializationFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD; case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_SPECIALIZATION_BATTLE_AXE; case BASE_ITEM_CLUB: return FEAT_WEAPON_SPECIALIZATION_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_SPECIALIZATION_CLUB; case BASE_ITEM_DAGGER: return FEAT_WEAPON_SPECIALIZATION_DAGGER; case BASE_ITEM_DART: return FEAT_WEAPON_SPECIALIZATION_DART; case BASE_ITEM_DIREMACE: return FEAT_WEAPON_SPECIALIZATION_DIRE_MACE; @@ -368,6 +449,7 @@ int GetEpicFocusFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_WEAPON_FOCUS_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_WEAPON_FOCUS_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_WEAPON_FOCUS_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_WEAPON_FOCUS_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_WEAPON_FOCUS_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_WEAPON_FOCUS_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_WEAPON_FOCUS_DIREMACE; @@ -439,6 +521,7 @@ int GetEpicSpecializationFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_WEAPON_SPECIALIZATION_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_WEAPON_SPECIALIZATION_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_WEAPON_SPECIALIZATION_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_WEAPON_SPECIALIZATION_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_WEAPON_SPECIALIZATION_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_WEAPON_SPECIALIZATION_DIREMACE; @@ -510,6 +593,7 @@ int GetImprovedCriticalFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_IMPROVED_CRITICAL_BASTARD_SWORD; case BASE_ITEM_BATTLEAXE: return FEAT_IMPROVED_CRITICAL_BATTLE_AXE; case BASE_ITEM_CLUB: return FEAT_IMPROVED_CRITICAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_IMPROVED_CRITICAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_IMPROVED_CRITICAL_DAGGER; case BASE_ITEM_DART: return FEAT_IMPROVED_CRITICAL_DART; case BASE_ITEM_DIREMACE: return FEAT_IMPROVED_CRITICAL_DIRE_MACE; @@ -581,6 +665,7 @@ int GetOverwhelmingCriticalFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_OVERWHELMING_CRITICAL_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_OVERWHELMING_CRITICAL_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_OVERWHELMING_CRITICAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_OVERWHELMING_CRITICAL_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_OVERWHELMING_CRITICAL_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_OVERWHELMING_CRITICAL_DIREMACE; @@ -652,6 +737,7 @@ int GetDevastatingCriticalFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_EPIC_DEVASTATING_CRITICAL_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_EPIC_DEVASTATING_CRITICAL_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_EPIC_DEVASTATING_CRITICAL_CLUB; case BASE_ITEM_DAGGER: return FEAT_EPIC_DEVASTATING_CRITICAL_DAGGER; case BASE_ITEM_DART: return FEAT_EPIC_DEVASTATING_CRITICAL_DART; case BASE_ITEM_DIREMACE: return FEAT_EPIC_DEVASTATING_CRITICAL_DIREMACE; @@ -718,6 +804,7 @@ int GetWeaponOfChoiceFeatOfWeaponType(int iWeaponType) case BASE_ITEM_BASTARDSWORD: return FEAT_WEAPON_OF_CHOICE_BASTARDSWORD; case BASE_ITEM_BATTLEAXE: return FEAT_WEAPON_OF_CHOICE_BATTLEAXE; case BASE_ITEM_CLUB: return FEAT_WEAPON_OF_CHOICE_CLUB; + case BASE_ITEM_CRAFTED_SCEPTER: return FEAT_WEAPON_OF_CHOICE_CLUB; case BASE_ITEM_DAGGER: return FEAT_WEAPON_OF_CHOICE_DAGGER; case BASE_ITEM_DIREMACE: return FEAT_WEAPON_OF_CHOICE_DIREMACE; case BASE_ITEM_DOUBLEAXE: return FEAT_WEAPON_OF_CHOICE_DOUBLEAXE; @@ -844,4 +931,6 @@ int PRCLargeWeaponCheck(int iBaseType, int nSize) } } return sTest != "" && sTest != IntToString(nSize); -} \ No newline at end of file +} + +//::void main(){} \ No newline at end of file diff --git a/src/include/prc_inc_function.nss b/src/include/prc_inc_function.nss index aa47614..a3b24e5 100644 --- a/src/include/prc_inc_function.nss +++ b/src/include/prc_inc_function.nss @@ -109,7 +109,7 @@ void SetupCharacterData(object oPC) case CLASS_TYPE_ARCANE_DUELIST: sScript = "prc_arcduel"; break; case CLASS_TYPE_ARCHIVIST: sScript = "prc_archivist"; iData |= 0x01; break; case CLASS_TYPE_ASSASSIN: break; - //case CLASS_TYPE_BAELNORN: sScript = "prc_baelnorn"; break; + //case CLASS_TYPE_BAELNORN: sScript = "prc_baelnorn"; break; case CLASS_TYPE_BARD: iData |= 0x07; break; case CLASS_TYPE_BATTLESMITH: sScript = "prc_battlesmith"; break; case CLASS_TYPE_BEGUILER: iData |= 0x03; break; @@ -431,7 +431,7 @@ void EvalPRCFeats(object oPC) ExecuteScript("moi_events", oPC); if (GetIsBinder(oPC)) - ExecuteScript("bnd_events", oPC); + ExecuteScript("bnd_events", oPC); // check if character with crafting feat has appropriate base item in her inventory // x - moved from prc_onhb_indiv.nss diff --git a/src/include/prc_inc_hextor.nss b/src/include/prc_inc_hextor.nss index affe360..531ba9a 100644 --- a/src/include/prc_inc_hextor.nss +++ b/src/include/prc_inc_hextor.nss @@ -1,4 +1,6 @@ #include "prc_feat_const" +#include "inc_item_props" +#include "prc_inc_spells" const string BRUTAL_STRIKE_MODE_VAR = "PRC_BRUTAL_STRIKE_MODE"; diff --git a/src/include/prc_inc_itmrstr.nss b/src/include/prc_inc_itmrstr.nss index 156f530..dd852f0 100644 --- a/src/include/prc_inc_itmrstr.nss +++ b/src/include/prc_inc_itmrstr.nss @@ -36,6 +36,8 @@ void CheckForPnPHolyAvenger(object oItem); #include "inc_utility" #include "prc_inc_newip" +#include "prc_inc_castlvl" +#include "inc_newspellbook" ////////////////////////////////////////////////// diff --git a/src/include/prc_inc_json.nss b/src/include/prc_inc_json.nss index 749c551..f15c675 100644 --- a/src/include/prc_inc_json.nss +++ b/src/include/prc_inc_json.nss @@ -18,11 +18,56 @@ //::////////////////////////////////////////////// #include "nw_inc_gff" #include "inc_debug" +#include "prc_inc_racial" +#include "prc_inc_nwscript" +#include "prc_inc_spells" +#include "prc_inc_util" +#include "prc_inc_fork" +#include "prc_inc_natweap" +//:: Get a random General feat. +void ApplyParagonBonusFeat(object oCreature, int iFeat); //::---------------------------------------------| //:: Helper functions | //::---------------------------------------------| +int GetHealerCompanionBonus(int nHealerLvl) +{ + // No bonus before 12th level + if (nHealerLvl < 12) + return 0; + + int nBonus = 0; + + // Non-epic improvements: 12, 15, 18, 21 (every 3 levels) + if (nHealerLvl >= 12) + { + int nPreEpicIntervals = ( (nHealerLvl < 21) ? (nHealerLvl - 12) : (21 - 12) ) / 3; + nBonus += 2 + (nPreEpicIntervals * 2); + } + + // Epic improvements: 24, 28, 32, 36... (every 4 levels) + if (nHealerLvl >= 24) + { + int nEpicIntervals = (nHealerLvl - 24) / 4; + // First epic improvement is +2 at 24 + nBonus += 2 + (nEpicIntervals * 2); + } + + return nBonus; +} + +/* int GetHealerCompanionBonus(int nHealerLvl) +{ + if (nHealerLvl < 12) + return 0; + + // Shift so that 12–14 yields interval 0 + int nIntervals = (nHealerLvl - 12) / 3; + + return 2 + (nIntervals * 2); +} */ + //:: Function to calculate the maximum possible hitpoints for oCreature int GetMaxPossibleHP(object oCreature) @@ -30,6 +75,8 @@ int GetMaxPossibleHP(object oCreature) int nMaxHP = 0; // Stores the total maximum hitpoints int i = 1; // Initialize position for class index int nConb = GetAbilityModifier(ABILITY_CONSTITUTION, oCreature); + int nRacial = MyPRCGetRacialType(oCreature); + int nSize = PRCGetCreatureSize(oCreature); // Loop through each class position the creature may have, checking each class in turn while (TRUE) @@ -54,8 +101,25 @@ int GetMaxPossibleHP(object oCreature) i++; } + if(nRacial == RACIAL_TYPE_CONSTRUCT || nRacial == RACIAL_TYPE_UNDEAD) + { + nConb = 0; + } + nMaxHP += nConb * GetHitDice(oCreature); + if(nRacial == RACIAL_TYPE_CONSTRUCT) + { + if(nSize == CREATURE_SIZE_FINE) nMaxHP += 0; + if(nSize == CREATURE_SIZE_DIMINUTIVE) nMaxHP += 0; + if(nSize == CREATURE_SIZE_TINY) nMaxHP += 0; + if(nSize == CREATURE_SIZE_SMALL) nMaxHP += 10; + if(nSize == CREATURE_SIZE_MEDIUM) nMaxHP += 20; + if(nSize == CREATURE_SIZE_LARGE) nMaxHP += 30; + if(nSize == CREATURE_SIZE_HUGE) nMaxHP += 40; + if(nSize == CREATURE_SIZE_GARGANTUAN) nMaxHP += 60; + } + return nMaxHP; } @@ -97,10 +161,564 @@ struct SizeModifiers int dexSkillMod; }; +//:: Returns ability mod for score +int GetAbilityModFromValue(int nAbilityValue) +{ + int nMod = (nAbilityValue - 10) / 2; + + // Adjust if below 10 and odd + if (nAbilityValue < 10 && (nAbilityValue % 2) != 0) + { + nMod = nMod - 1; + } + return nMod; +} + +//:: Get a random General feat. +void PickParagonBonusFeat(object oCreature) +{ +//:: Paragon creatures get a +15 to all ability scores, +//:: so can always meet feat pre-reqs. + +//:: Detect spellcasting classes (FOR FUTURE USE) + int i; + for (i = 1; i <= 8; i++) + { + if (GetIsArcaneClass(GetClassByPosition(i, oCreature))) + { + SetLocalInt(oCreature, "ParagonArcaneCaster", 0); + } + if (GetIsDivineClass(GetClassByPosition(i, oCreature))) + { + SetLocalInt(oCreature, "ParagonDivineCaster", 0); + } + } + switch (Random(18)) + { + //:: Dodge -> Mobility -> Spring Attack + case 0: + { + int iDodge = GetHasFeat(FEAT_DODGE, oCreature); + int iMobility = GetHasFeat(FEAT_MOBILITY, oCreature); + int iSpringAttack = GetHasFeat(FEAT_SPRING_ATTACK, oCreature); + + //:: Grant only the first missing feat in the chain + if (iDodge == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_DODGE); + } + else if (iMobility == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_MOBILITY); + } + else if (iSpringAttack == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_SPRING_ATTACK); + } + } + break; + //:: Power Attack -> Cleave -> Imp Power Attack -> Great Cleave + case 1: + { + int iPower = GetHasFeat(FEAT_POWER_ATTACK, oCreature); + int iCleave = GetHasFeat(FEAT_CLEAVE, oCreature); + int iImpPower = GetHasFeat(FEAT_IMPROVED_POWER_ATTACK, oCreature); + int iGrCleave = GetHasFeat(FEAT_GREAT_CLEAVE, oCreature); + + //:: Grant only the first missing feat in the chain + if (iPower == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_POWER_ATTACK); + } + else if (iCleave == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_CLEAVE); + } + else if (iImpPower == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_POWER_ATTACK); + } + else if (iGrCleave == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_GREAT_CLEAVE); + } + } + break; + //:: Expertise -> Imp Expertise -> Whirlwind Attack -> Imp Whirlwind Attack + case 2: + { + int iEx = GetHasFeat(FEAT_EXPERTISE, oCreature); + int iImpEx = GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature); + int iWhirl = GetHasFeat(FEAT_WHIRLWIND_ATTACK, oCreature); + int iImpWhirl = GetHasFeat(FEAT_IMPROVED_WHIRLWIND, oCreature); + + //:: Grant only the first missing feat in the chain + if (iEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_EXPERTISE); + } + else if (iImpEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_EXPERTISE); + } + else if (iWhirl == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_WHIRLWIND_ATTACK); + } + else if (iImpWhirl == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_WHIRLWIND); + } + } + break; + //:: Disarm -> Expertise -> Improved Disarm -> Imp Expertise + case 3: + { + int iDisarm = GetHasFeat(FEAT_DISARM, oCreature); + int iEx = GetHasFeat(FEAT_EXPERTISE, oCreature); + int iImpDisarm = GetHasFeat(FEAT_IMPROVED_DISARM, oCreature); + int iImpEx = GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature); + + //:: Grant only the first missing feat in the chain + if (iDisarm == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_DISARM); + } + else if (iEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_EXPERTISE); + } + else if (iImpDisarm == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_DISARM); + } + else if (iImpEx == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_EXPERTISE); + } + } + break; + //:: Toughness + case 4: + { + ApplyParagonBonusFeat(oCreature, FEAT_TOUGHNESS); + } + break; + //:: Great Fortitude + case 5: + { + ApplyParagonBonusFeat(oCreature, FEAT_GREAT_FORTITUDE); + } + break; + //:: Lightining Reflexes + case 6: + { + ApplyParagonBonusFeat(oCreature, FEAT_LIGHTNING_REFLEXES); + } + break; + //:: Iron Will -> Unnatural Will + case 7: + { + int iIronWill = GetHasFeat(FEAT_IRON_WILL, oCreature); + int iUnnaturalWill = GetHasFeat(FEAT_UNNATURAL_WILL, oCreature); + + //:: Grant only the first missing feat in the chain + if (iIronWill == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_IRON_WILL); + } + else if (iUnnaturalWill == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_UNNATURAL_WILL); + } + } + break; + //:: Blind-Fight + case 8: + { + ApplyParagonBonusFeat(oCreature, FEAT_BLIND_FIGHT); + } + break; + //:: Improved Initiative + case 9: + { + ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_INITIATIVE); + } + break; + //:: Alertness + case 10: + { + ApplyParagonBonusFeat(oCreature, FEAT_ALERTNESS); + } + break; + //:: Blooded + case 11: + { + ApplyParagonBonusFeat(oCreature, FEAT_BLOODED); + } + break; + //:: Side-step Charge + case 12: + { + ApplyParagonBonusFeat(oCreature, FEAT_SIDESTEP_CHARGE); + } + break; + //:: Thug + case 13: + { + ApplyParagonBonusFeat(oCreature, FEAT_THUG); + } + break; + //:: Dive for Cover + case 14: + { + ApplyParagonBonusFeat(oCreature, FEAT_DIVE_FOR_COVER); + } + break; + //:: Endurance -> Strong Stomach + case 15: + { + int iEndurance = GetHasFeat(FEAT_ENDURANCE, oCreature); + int iStrStomach = GetHasFeat(FEAT_STRONG_STOMACH, oCreature); + + //:: Grant only the first missing feat in the chain + if (iEndurance == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_ENDURANCE); + } + else if (iStrStomach == 0) + { + ApplyParagonBonusFeat(oCreature, FEAT_STRONG_STOMACH); + } + } + break; + //:: Resist Disease + case 16: + { + ApplyParagonBonusFeat(oCreature, FEAT_RESIST_DISEASE); + } + break; + //:: Resist Poison + case 17: + { + ApplyParagonBonusFeat(oCreature, FEAT_RESIST_POISON); + } + break; + } +} + +//:: Check & apply the feat using EffectBonusFeat if it +//:: doesn't exist on the creature already +void ApplyParagonBonusFeat(object oCreature, int iFeat) +{ + // If the creature does not already have the feat, apply it + if (!GetHasFeat(iFeat, oCreature)) + { + effect eFeat = EffectBonusFeat(iFeat); + effect eLink = UnyieldingEffect(eFeat); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oCreature); + } + else + { + DelayCommand(0.0f, PickParagonBonusFeat(oCreature)); + } +} + +//:: Apply Paragon effects to a non-PC creature +void ApplyParagonEffects(object oCreature, int nBaseHD, int nBaseCR) +{ +//:: Declare major variables + int nNewCR; + + effect eParagon; + +//:: Set maximum hit points for each HD + int nParagonHP = (GetMaxPossibleHP(oCreature) + (nBaseHD * GetAbilityModifier(ABILITY_CONSTITUTION, oCreature))); + SetCurrentHitPoints(oCreature, nParagonHP); + +//:: Tripling the speed for all movement types + eParagon = EffectLinkEffects(eParagon, EffectMovementSpeedIncrease(300)); + +//:: +25 luck bonus on all attack rolls + eParagon = EffectLinkEffects(eParagon, EffectAttackIncrease(25)); + +//:: +20 luck bonus on damage rolls for melee and thrown ranged attacks + eParagon = EffectLinkEffects(eParagon, EffectDamageIncrease(20)); + +//:: AC Bonuses: +12 insight, +12 luck + eParagon = EffectLinkEffects(eParagon, EffectACIncrease(12, AC_DODGE_BONUS)); + eParagon = EffectLinkEffects(eParagon, EffectACIncrease(12, AC_DEFLECTION_BONUS)); + +//:: Boost caster & SLA level by 15 + SetLocalInt(oCreature, PRC_CASTERLEVEL_ADJUSTMENT, 15); + +//:: Fire and cold resistance 10, or keep the higher existing resistance if applicable + eParagon = EffectLinkEffects(eParagon, EffectDamageResistance(DAMAGE_TYPE_FIRE, 10)); + eParagon = EffectLinkEffects(eParagon, EffectDamageResistance(DAMAGE_TYPE_COLD, 10)); + +//:: Damage Reduction 20/epic or retain existing DR if higher + eParagon = EffectLinkEffects(eParagon, EffectDamageReduction(20, DAMAGE_POWER_ENERGY)); + +//:: Spell Resistance equal to CR +10, or retain existing SR if higher + int iExSR = GetSpellResistance(oCreature); + int nSpellResistance; + + if (iExSR < nBaseCR + 10) + { + nSpellResistance = nBaseCR + 10; + } + else + { + nSpellResistance = 0; + } + + eParagon = EffectLinkEffects(eParagon, EffectSpellResistanceIncrease(nSpellResistance)); + +//:: Fast Healing 20 + eParagon = EffectLinkEffects(eParagon, EffectRegenerate(20, 6.0f)); + +//:: Saving Throws: +10 insight bonus on all saving throws + eParagon = EffectLinkEffects(eParagon, EffectSavingThrowIncrease(SAVING_THROW_ALL, 10)); + +//:: Skills: +10 competence bonus to all skill checks + int nSkillID = 0; + + while (TRUE) + { + //:: Get & check skill + string sSkillLabel = Get2DACache("skills", "Label", nSkillID); + + //:: Break when out of skills + if (sSkillLabel == "") + break; + + //:: Apply the skill increase effect for the current skill + eParagon = EffectLinkEffects(eParagon, EffectSkillIncrease(nSkillID, 10)); + + + //:: Move to the next skill ID + nSkillID++; + } + +//:: Two free general feats. + PickParagonBonusFeat(oCreature); + PickParagonBonusFeat(oCreature); + + eParagon = UnyieldingEffect(eParagon); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eParagon, oCreature); +} + +// Build and return all effects for the Celestial Template +effect CelestialTemplateEffects(int nHD) +{ + int nResist; + int nDRAmount; + int nDRBypass; + + // ------------------------- + // Elemental Resistances + // ------------------------- + // 1–7 HD = 5 + // 8+ HD = 10 + if (nHD >= 8) + { + nResist = 10; + } + else + { + nResist = 5; + } + + // ------------------------- + // Damage Reduction + // ------------------------- + // 1–3 HD = none + // 4–11 HD = 5/magic + // 12+ HD = 10/magic + if (nHD >= 12) + { + nDRAmount = 10; + nDRBypass = DAMAGE_POWER_PLUS_ONE; // DR 10/magic + } + else if (nHD >= 4) + { + nDRAmount = 5; + nDRBypass = DAMAGE_POWER_PLUS_ONE; // DR 5/magic + } + else + { + nDRAmount = 0; + nDRBypass = 0; // no DR + } + + // ------------------------- + // Build Effects + // ------------------------- + effect eEffects; + effect eRes; + + // Acid + eRes = EffectDamageResistance(DAMAGE_TYPE_ACID, nResist, 0); + eEffects = eRes; + + // Cold + eRes = EffectDamageResistance(DAMAGE_TYPE_COLD, nResist, 0); + eEffects = EffectLinkEffects(eEffects, eRes); + + // Electricity + eRes = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nResist, 0); + eEffects = EffectLinkEffects(eEffects, eRes); + + // DR if any + if (nDRAmount > 0) + { + effect eDR = EffectDamageReduction(nDRAmount, nDRBypass, 0); + eEffects = EffectLinkEffects(eEffects, eDR); + } + + eEffects = UnyieldingEffect(eEffects); + + return eEffects; +} + + + +void ReallyEquipItemInSlot(object oNPC, object oItem, int nSlot) +{ + if (GetItemInSlot(nSlot) != oItem) + { + //ClearAllActions(); + AssignCommand(oNPC, ActionEquipItem(oItem, nSlot)); + DelayCommand(0.5, ReallyEquipItemInSlot(oNPC, oItem, nSlot)); + } +} + +// Get the size of a JSON array +int GetJsonArraySize(json jArray) +{ + int iSize = 0; + while (JsonArrayGet(jArray, iSize) != JsonNull()) + { + iSize++; + } + return iSize; +} + +int CheckForWeapon(object oCreature) +{ + if (GetIsWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature)) == 1 || GetIsWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature)) == 1) + { + // oCreature has a weapon in at least one hand + return TRUE; + } + else + { + // oCreature doesn't have a weapon in either hand + return FALSE; + } +} + +//:: Adds Psuedonatural resistances & DR. +void ApplyPseudonaturalEffects(object oCreature) +{ + if(!GetIsObjectValid(oCreature)) return; + + int nHD = GetHitDice(oCreature); + if(DEBUG) DoDebug("prc_inc_json >> ApplyPseudonaturalEffects: nHD is: "+IntToString(nHD)+"."); + // ------------------------- + // Spell Resistance + // SR = 10 + HD (max 25) + // ------------------------- + int nSR = 10 + nHD; + if(nSR > 25) nSR = 25; + + effect eSR = EffectSpellResistanceIncrease(nSR); + eSR = TagEffect(eSR, "PSEUDO_SR"); + eSR = EffectLinkEffects(eSR, UnyieldingEffect(eSR)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSR, oCreature); + + // ------------------------- + // Acid/Electricity Resistance + // Reference Table: + // HD 1–3 : Resist 5 + // HD 4–7 : Resist 5 + // HD 8–11 : Resist 10 + // HD >=12 : Resist 15 + // ------------------------- + int nResist; + + if(nHD <= 7) nResist = 5; + else if(nHD <=11) nResist = 10; + else nResist = 15; + + effect eResAcid = EffectDamageResistance(DAMAGE_TYPE_ACID, nResist); + eResAcid = TagEffect(eResAcid, "PSEUDO_RES_ACID"); + eResAcid = EffectLinkEffects(eResAcid, UnyieldingEffect(eResAcid)); + + effect eResElec = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nResist); + eResElec = TagEffect(eResElec, "PSEUDO_RES_ELEC"); + eResElec = EffectLinkEffects(eResElec, UnyieldingEffect(eResElec)); + + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eResAcid, oCreature); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eResElec, oCreature); + + // ------------------------- + // Damage Reduction + // Reference Table: + // HD 1–3 : none + // HD 4–7 : DR 5 / magic + // HD 8–11 : DR 5 / magic + // HD >=12 : DR 10 / magic + // ------------------------- + + int nDR; + if(nHD <= 3) { nDR = 0; } + else if(nHD <= 11) { nDR = 5; } + else { nDR = 10; } + + effect eDR = EffectDamageReduction(nDR, DAMAGE_POWER_PLUS_ONE, 0, FALSE); + eDR = TagEffect(eDR, "PSEUDO_DR_MAGIC"); + eDR = EffectLinkEffects(eDR, UnyieldingEffect(eDR)); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDR, oCreature); +} + + //::---------------------------------------------| //:: JSON functions | //::---------------------------------------------| +//:: Returns the Constitution value from a GFF creature UTC +int json_GetCONValue(json jCreature) +{ + int nCon = 0; // default if missing + + // Check if the Con field exists + if (GffGetFieldExists(jCreature, "Con")) + { + nCon = JsonGetInt(GffGetByte(jCreature, "Con")); + } + + return nCon; +} + +//:: Returns the Challenge Rating from a GFF creature UTC +float json_GetChallengeRating(json jCreature) +{ + float fCR = 0.25; // default if missing + + if (GffGetFieldExists(jCreature, "ChallengeRating")) + { + json jCR = GffGetFloat(jCreature, "ChallengeRating"); + if (jCR != JsonNull()) + { + fCR = JsonGetFloat(jCR); + } + } + + return fCR; +} + //:: Returns the integer value of a VarTable entry named sVarName, or 0 if not found. int json_GetLocalIntFromVarTable(json jCreature, string sVarName) { @@ -141,12 +759,52 @@ int json_GetLocalIntFromVarTable(json jCreature, string sVarName) return 0; } -//:: Returns the total Hit Dice from a JSON creature GFF. -int json_GetCreatureHD(json jGff) +//:: Returns the string value of a VarTable entry named sVarName, or "" if not found. +string json_GetLocalStringFromVarTable(json jCreature, string sVarName) +{ + json jVarTable = GffGetList(jCreature, "VarTable"); + if (jVarTable == JsonNull()) + return ""; + + int nCount = JsonGetLength(jVarTable); + int i; + for (i = 0; i < nCount; i++) + { + json jEntry = JsonArrayGet(jVarTable, i); + if (jEntry == JsonNull()) continue; + + // Get the Name field using GFF functions + json jName = GffGetString(jEntry, "Name"); + if (jName == JsonNull()) continue; + string sName = JsonGetString(jName); + + if (sName == sVarName) + { + // Get the Type field to verify it's a string + json jType = GffGetDword(jEntry, "Type"); + if (jType != JsonNull()) + { + int nType = JsonGetInt(jType); + if (nType == 3) // Type 3 = string + { + // Get the Value field using GFF functions + json jValue = GffGetString(jEntry, "Value"); + if (jValue == JsonNull()) return ""; + return JsonGetString(jValue); + } + } + } + } + + return ""; +} + +//:: Returns the total Hit Dice from a JSON'd creature GFF. +int json_GetCreatureHD(json jCreature) { int nHD = 0; - json jClasses = GffGetList(jGff, "ClassList"); + json jClasses = GffGetList(jCreature, "ClassList"); if (jClasses == JsonNull()) return 0; @@ -170,8 +828,61 @@ int json_GetCreatureHD(json jGff) return nHD; } +json json_RecalcMaxHP(json jCreature, int iHitDieValue) +{ + int iHD = json_GetCreatureHD(jCreature); + + //:: Retrieve the RacialType field + json jRacialTypeField = JsonObjectGet(jCreature, "Race"); + int nRacialType = JsonGetInt(jRacialTypeField); + + //:: Retrieve the CreatureSize from the creature appearance field + json jAppearanceField = JsonObjectGet(jCreature, "Appearance_Type"); + int nAppearance = JsonGetInt(jAppearanceField); + + int nSize = StringToInt(Get2DAString("appearance", "SizeCategory", nAppearance)); + + //CEP adds other sizes, take them into account too + if(nSize == 20) + nSize = CREATURE_SIZE_DIMINUTIVE; + else if(nSize == 21) + nSize = CREATURE_SIZE_FINE; + else if(nSize == 22) + nSize = CREATURE_SIZE_GARGANTUAN; + else if(nSize == 23) + nSize = CREATURE_SIZE_COLOSSAL; + + int iNewMaxHP = (iHitDieValue * iHD); + + if(nRacialType == RACIAL_TYPE_CONSTRUCT) + { + if(nSize == CREATURE_SIZE_FINE) iNewMaxHP += 0; + if(nSize == CREATURE_SIZE_DIMINUTIVE) iNewMaxHP += 0; + if(nSize == CREATURE_SIZE_TINY) iNewMaxHP += 0; + if(nSize == CREATURE_SIZE_SMALL) iNewMaxHP += 10; + if(nSize == CREATURE_SIZE_MEDIUM) iNewMaxHP += 20; + if(nSize == CREATURE_SIZE_LARGE) iNewMaxHP += 30; + if(nSize == CREATURE_SIZE_HUGE) iNewMaxHP += 40; + if(nSize == CREATURE_SIZE_GARGANTUAN) iNewMaxHP += 60; + } + + if(DEBUG) DoDebug("prc_inc_json >> json_RecalcMaxHP | New MaxHP is: "+IntToString(iNewMaxHP)+ "."); + + jCreature = GffReplaceShort(jCreature, "MaxHitPoints", iNewMaxHP); + jCreature = GffReplaceShort(jCreature, "CurrentHitPoints", iNewMaxHP); + jCreature = GffReplaceShort(jCreature, "HitPoints", iNewMaxHP); + +/* SendMessageToPC(GetFirstPC(), "HD = " + IntToString(iHD)); + SendMessageToPC(GetFirstPC(), "HitDieValue = " + IntToString(iHitDieValue)); + SendMessageToPC(GetFirstPC(), "CON = " + IntToString(iCON)); + SendMessageToPC(GetFirstPC(), "Mod = " + IntToString(iMod)); + SendMessageToPC(GetFirstPC(), "New HP = " + IntToString(iNewMaxHP)); */ + + return jCreature; +} + //:: Reads ABILITY_TO_INCREASE from creature's VarTable and applies stat boosts based on increased HD -json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifierCap) +json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD) { if (jCreature == JsonNull()) return jCreature; @@ -180,7 +891,7 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifier int nAbilityToIncrease = json_GetLocalIntFromVarTable(jCreature, "ABILITY_TO_INCREASE"); if (nAbilityToIncrease < 0 || nAbilityToIncrease > 5) { - if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Invalid ABILITY_TO_INCREASE value: " + IntToString(nAbilityToIncrease)); + DoDebug("json_ApplyAbilityBoostFromHD: Invalid ABILITY_TO_INCREASE value: " + IntToString(nAbilityToIncrease)); return jCreature; // Invalid ability index } @@ -188,7 +899,7 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifier json jClassList = GffGetList(jCreature, "ClassList"); if (jClassList == JsonNull()) { - if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Failed to get ClassList"); + DoDebug("json_ApplyAbilityBoostFromHD: Failed to get ClassList"); return jCreature; } @@ -208,10 +919,12 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifier } } } + + if(DEBUG) DoDebug("prc_inc_json >> json_ApplyAbilityBoostFromHD: nCurrentTotalHD = "+IntToString(nCurrentTotalHD)+"."); if (nCurrentTotalHD <= 0) { - if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: No valid Hit Dice found"); + DoDebug("json_ApplyAbilityBoostFromHD: No valid Hit Dice found"); return jCreature; } @@ -257,7 +970,7 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifier // Clamp to valid byte range if (nNewScore < 1) nNewScore = 1; - if (nNewScore > 255) nNewScore = 255; + if (nNewScore > 250) nNewScore = 250; if(DEBUG) DoDebug("json_ApplyAbilityBoostFromHD: Increasing " + sAbilityField + " from " + IntToString(nCurrentScore) + " to " + IntToString(nNewScore)); @@ -273,7 +986,7 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD, int nModifier return jCreature; } -//:: Adjust a skill by its ID (more efficient than name lookup) +//:: Adjust a skill by its ID json json_AdjustCreatureSkillByID(json jCreature, int nSkillID, int nMod) { // Get the SkillList @@ -313,7 +1026,7 @@ json json_AdjustCreatureSkillByID(json jCreature, int nSkillID, int nMod) // Clamp to valid range if (nNewRank < 0) nNewRank = 0; - if (nNewRank > 255) nNewRank = 255; + if (nNewRank > 127) nNewRank = 127; // Update the rank in the skill struct jSkill = GffReplaceByte(jSkill, "Rank", nNewRank); @@ -372,7 +1085,7 @@ json json_AddFeatsFromCreatureVars(json jCreature, int nOriginalHD) if(DEBUG) DoDebug("json_AddFeatsFromCreatureVars: Original feat count: " + IntToString(nOriginalFeatCount)); int nAdded = 0; - int i = 1; + int i = 0; int nMaxIterations = 100; // Safety valve int nIterations = 0; @@ -470,7 +1183,7 @@ int json_GetArraySize(json jArray) return iSize; } -//:: Directly modifies oCreature's Base Natural AC if iNewAC is higher. +//:: Directly updates oCreature's Base Natural AC if iNewAC is higher. //:: json json_UpdateBaseAC(json jCreature, int iNewAC) { @@ -493,6 +1206,26 @@ json json_UpdateBaseAC(json jCreature, int iNewAC) } } +//:: Increases jCreature's Natural AC by iAddAC. +//:: +json json_IncreaseBaseAC(json jCreature, int iAddAC) +{ + json jBaseAC = GffGetByte(jCreature, "NaturalAC"); + + if (jBaseAC == JsonNull()) + { + return JsonNull(); + } + else + { + int nBaseAC = JsonGetInt(jBaseAC); // convert JSON number -> int + int nNewAC = nBaseAC + iAddAC; + + jCreature = GffReplaceByte(jCreature, "NaturalAC", nNewAC); + return jCreature; + } +} + //:: Directly modifies jCreature's Challenge Rating. //:: This is useful for most XP calculations. json json_UpdateCR(json jCreature, int nBaseCR, int nCRMod) @@ -510,8 +1243,7 @@ json json_UpdateCR(json jCreature, int nBaseCR, int nCRMod) //:: Directly modifies ability scores in a creature's JSON GFF. //:: -json json_UpdateTemplateStats(json jCreature, int iModStr = 0, int iModDex = 0, int iModCon = 0, - int iModInt = 0, int iModWis = 0, int iModCha = 0) +json json_UpdateTemplateStats(json jCreature, int iModStr = 0, int iModDex = 0, int iModCon = 0, int iModInt = 0, int iModWis = 0, int iModCha = 0) { int iCurrent; @@ -590,7 +1322,6 @@ json json_AddHitDice(json jCreature, int nAmount) // Grab the first class entry json jFirstClass = JsonArrayGet(jClasses, 0); - // Only touch ClassLevel; do NOT modify Class type json jCurrentLevel = GffGetShort(jFirstClass, "ClassLevel"); int nCurrentLevel = JsonGetInt(jCurrentLevel); int nNewLevel = nCurrentLevel + nAmount; @@ -608,7 +1339,7 @@ json json_AddHitDice(json jCreature, int nAmount) } //:: Adjusts a creature's size by nSizeChange (-4 to +4) and updates ability scores accordingly. -json json_AdjustCreatureSize(json jCreature, int nSizeDelta) +json json_AdjustCreatureSize(json jCreature, int nSizeDelta, int nIncorporeal = FALSE) { if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Entering function. nSizeDelta=" + IntToString(nSizeDelta)); @@ -642,6 +1373,11 @@ json json_AdjustCreatureSize(json jCreature, int nSizeDelta) int conMod = nSteps * 2; int naturalAC = nSteps * 1; int dexSkillMod = nSteps * -2; + + if(nIncorporeal) + { + strMod = 0; + } if(DEBUG) DoDebug("prc_inc_json >> json_AdjustCreatureSize: Applying stat modifiers: STR=" + IntToString(strMod) + " DEX=" + IntToString(dexMod) + @@ -745,6 +1481,513 @@ json json_AdjustCreatureSize(json jCreature, int nSizeDelta) return jCreature; } +//:: Changes jCreature's creature type. +json json_ModifyRacialType(json jCreature, int nNewRacialType) +{ + if(DEBUG)DoDebug("prc_inc_json >> json_ModifyRacialType: Entering function"); + + // Retrieve the RacialType field + json jRacialTypeField = JsonObjectGet(jCreature, "Race"); + + if (JsonGetType(jRacialTypeField) == JSON_TYPE_NULL) + { + DoDebug("prc_inc_json >> json_ModifyRacialType: JsonGetType error 1: " + JsonGetError(jRacialTypeField)); + //SpeakString("JsonGetType error 1: " + JsonGetError(jRacialTypeField)); + return JsonNull(); + } + + // Retrieve the value to modify + json jRacialTypeValue = JsonObjectGet(jRacialTypeField, "value"); + + if (JsonGetType(jRacialTypeValue) != JSON_TYPE_INTEGER) + { + DoDebug("prc_inc_json >> json_ModifyRacialType: JsonGetType error 2: " + JsonGetError(jRacialTypeValue)); + //SpeakString("JsonGetType error 2: " + JsonGetError(jRacialTypeValue)); + return JsonNull(); + } + + jCreature = GffReplaceByte(jCreature, "Race", nNewRacialType); + + // Return the new creature object + return jCreature; +} + +//:: Updates CR for Celestial template +json json_UpdateCelestialCR(json jCreature, int nBaseCR, int nHD) +{ + int nNewCR; + + //:: Calculate CR based on HD + if (nHD <= 3) + { + nNewCR = nBaseCR; + } + else if (nHD <= 7) + { + nNewCR = nBaseCR + 1; + } + else + { + nNewCR = nBaseCR + 2; + } + + //:: Modify Challenge Rating + jCreature = GffReplaceFloat(jCreature, "ChallengeRating", IntToFloat(nNewCR)); + return jCreature; +} + +//:: Adds Celestial SLA's to creature +json json_AddCelestialPowers(json jCreature) +{ + // Get the existing SpecAbilityList + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + if (jSpecAbilityList == JsonNull()) + { + jSpecAbilityList = JsonArray(); + } + + //:: Add Smite Evil 1x / day + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", SPELLABILITY_SMITE_EVIL); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", json_GetCreatureHD(jCreature)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + + //:: Add the list to the creature + jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); + return jCreature; +} + +//:: Apply Celestial template to a creature JSON template +json json_MakeCelestial(json jCreature, int nBaseHD, int nBaseCR) +{ + if (jCreature == JsonNull()) + return JsonNull(); + + //:: Get current HD for scaling + int nHD = json_GetCreatureHD(jCreature); + if (nHD <= 0) + { + DoDebug("prc_inc_json >> json_MakeCelestial: Invalid HD"); + return JsonNull(); + } + + //:: Get current CR + float fCR = json_GetChallengeRating(jCreature); + + //:: Update CR using Celestial formula + jCreature = json_UpdateCelestialCR(jCreature, FloatToInt(fCR), nHD); + if (jCreature == JsonNull()) + { + DoDebug("prc_inc_json >> json_MakeCelestial: json_UpdateCelestialCR failed"); + return JsonNull(); + } + + //:: Ensure Intelligence is at least 4 + json jInt = GffGetByte(jCreature, "Int"); + if (jInt != JsonNull() && JsonGetInt(jInt) < 4) + { + jCreature = GffReplaceByte(jCreature, "Int", 4); + } + + //:: Add celestial Smite Evil + jCreature = json_AddCelestialPowers(jCreature); + if (jCreature == JsonNull()) + { + DoDebug("prc_inc_json >> json_MakeCelestial: json_AddCelestialPowers failed"); + return JsonNull(); + } + + //:: Change creature type if animal/beast/vermin to magical beast + int nRacialType = JsonGetInt(GffGetByte(jCreature, "Race")); + if (nRacialType == RACIAL_TYPE_ANIMAL || nRacialType == RACIAL_TYPE_VERMIN || nRacialType == RACIAL_TYPE_BEAST) + { + jCreature = json_ModifyRacialType(jCreature, RACIAL_TYPE_MAGICAL_BEAST); + } + + //:: Update creature CR + jCreature = json_UpdateCelestialCR(jCreature, nBaseCR, nHD); + if (jCreature == JsonNull()) + { + DoDebug("prc_inc_json >> json_MakeCelestial: json_UpdateCelestialCR failed"); + return JsonNull(); + } + + return jCreature; +} + +//:: Spawns a Celestial Companion from a template +object MakeCelestialCompanionFromTemplate(string sResref, location lSpawnLoc, int nHealerLvl) +{ + int nBonus = GetHealerCompanionBonus(nHealerLvl); + + json jCelestial = TemplateToJson(sResref, RESTYPE_UTC); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate: TemplateToJson failed — bad resref or resource missing."); + return OBJECT_INVALID; + } + + //:: Get local vars to transfer over. + int iMinHD = json_GetLocalIntFromVarTable(jCelestial, "iMinHD"); + int iMaxHD = json_GetLocalIntFromVarTable(jCelestial, "iMaxHD"); + int nOriginalHD = json_GetLocalIntFromVarTable(jCelestial, "nOriginalHD"); + int iClass2 = json_GetLocalIntFromVarTable(jCelestial, "Class2"); + int iClass2Package = json_GetLocalIntFromVarTable(jCelestial, "Class2Package"); + int iClass2Start = json_GetLocalIntFromVarTable(jCelestial, "Class2Start"); + int iBaseCL = json_GetLocalIntFromVarTable(jCelestial, "iBaseCL"); + int iMagicUse = json_GetLocalIntFromVarTable(jCelestial, "X2_L_BEH_MAGIC"); + string sAI = json_GetLocalStringFromVarTable(jCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT"); + + //:: Get the original Challenge Rating + int nBaseCR = FloatToInt(json_GetChallengeRating(jCelestial)); + + //:: Apply celestial template modifications + jCelestial = json_MakeCelestial(jCelestial, nBonus, nBaseCR); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate failed — json_MakeCelestial returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: Apply +2 Natural AC bonus per 3 Healer levels + jCelestial = json_IncreaseBaseAC(jCelestial, nBonus); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate failed — json_IncreaseBaseAC returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: +2 STR, DEX & INT per 3 Healer levels + jCelestial = json_UpdateTemplateStats(jCelestial, nBonus, nBonus, 0, nBonus, 0, 0); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCompanionFromTemplate failed — json_UpdateTemplateStats returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: The Companion always has Improved Evasion if the healer qualifies, + //:: but adding it this way gives the base creature more utility for builders. + if (nHealerLvl > 7) + { + //:: Add Improved Evasion feat directly to FeatList + json jFeatList = GffGetList(jCelestial, "FeatList"); + if (jFeatList == JsonNull()) + jFeatList = JsonArray(); + + //:: Check if creature already has Improved Evasion + int bHasFeat = FALSE; + int nFeatCount = JsonGetLength(jFeatList); + int j; + + for (j = 0; j < nFeatCount; j++) + { + json jFeatStruct = JsonArrayGet(jFeatList, j); + if (jFeatStruct != JsonNull()) + { + json jFeatValue = GffGetWord(jFeatStruct, "Feat"); + if (jFeatValue != JsonNull() && JsonGetInt(jFeatValue) == FEAT_IMPROVED_EVASION) + { + bHasFeat = TRUE; + break; + } + } + } + + //:: Add feat only if not already present + if (!bHasFeat) + { + json jNewFeat = JsonObject(); + jNewFeat = JsonObjectSet(jNewFeat, "__struct_id", JsonInt(1)); + jNewFeat = GffAddWord(jNewFeat, "Feat", FEAT_IMPROVED_EVASION); + + jFeatList = JsonArrayInsert(jFeatList, jNewFeat); + jCelestial = GffReplaceList(jCelestial, "FeatList", jFeatList); + } + } + + //:: Spawn the creature + object oCelestial = JsonToObject(jCelestial, lSpawnLoc); + + //:: Set variables for LevelUpSummon() + SetLocalInt(oCelestial, "TEMPLATE_CELESTIAL", 1); + SetLocalInt(oCelestial, "iMinHD", iMinHD); + SetLocalInt(oCelestial, "iMaxHD", iMaxHD); + SetLocalInt(oCelestial, "nOriginalHD", nOriginalHD); + SetLocalInt(oCelestial, "Class2", iClass2); + SetLocalInt(oCelestial, "Class2Package", iClass2Package); + SetLocalInt(oCelestial, "Class2Start", iClass2Start); + SetLocalInt(oCelestial, "iBaseCL", iBaseCL); + SetLocalInt(oCelestial, "X2_L_BEH_MAGIC", iMagicUse); + SetLocalString(oCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT", sAI); + + return oCelestial; +} + +//:: Spawns a Celestial creature from a template +object MakeCelestialCreatureFromTemplate(string sResref, location lSpawnLoc) +{ + json jCelestial = TemplateToJson(sResref, RESTYPE_UTC); + if (jCelestial == JSON_NULL) + { + DoDebug("prc_inc_json >> MakeCelestialCreatureFromTemplate: TemplateToJson failed — bad resref or resource missing."); + return OBJECT_INVALID; + } + + //:: Get current HD + int nCurrentHD = json_GetCreatureHD(jCelestial); + if (nCurrentHD <= 0) + { + DoDebug("make_celestial >> MakeCelestialCreatureFromTemplate failed — template missing HD data."); + return OBJECT_INVALID; + } + + //:: Get current CR + int nBaseCR = 1; + nBaseCR = FloatToInt(json_GetChallengeRating(jCelestial)); + if (nBaseCR <= 0) + { + DoDebug("make_celestial >> MakeCelestialCreatureFromTemplate failed — template missing CR data."); + return OBJECT_INVALID; + } + + //:: Get local vars to transfer over. + int iMinHD = json_GetLocalIntFromVarTable(jCelestial, "iMinHD"); + int iMaxHD = json_GetLocalIntFromVarTable(jCelestial, "iMaxHD"); + int nOriginalHD = json_GetLocalIntFromVarTable(jCelestial, "nOriginalHD"); + int iClass2 = json_GetLocalIntFromVarTable(jCelestial, "Class2"); + int iClass2Package = json_GetLocalIntFromVarTable(jCelestial, "Class2Package"); + int iClass2Start = json_GetLocalIntFromVarTable(jCelestial, "Class2Start"); + int iBaseCL = json_GetLocalIntFromVarTable(jCelestial, "iBaseCL"); + int iMagicUse = json_GetLocalIntFromVarTable(jCelestial, "X2_L_BEH_MAGIC"); + string sAI = json_GetLocalStringFromVarTable(jCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT"); + + //:: Apply celestial template modifications + jCelestial = json_MakeCelestial(jCelestial, nCurrentHD, nBaseCR); + if (jCelestial == JSON_NULL) + { + DoDebug("make_celestial >> MakeCelestialCreatureFromTemplate failed — MakeCelestial returned invalid JSON."); + return OBJECT_INVALID; + } + + //:: Spawn the creature + object oCelestial = JsonToObject(jCelestial, lSpawnLoc); + + //:: Set variables + SetLocalInt(oCelestial, "TEMPLATE_CELESTIAL", 1); + SetLocalInt(oCelestial, "iMinHD", iMinHD); + SetLocalInt(oCelestial, "iMaxHD", iMaxHD); + SetLocalInt(oCelestial, "nOriginalHD", nOriginalHD); + SetLocalInt(oCelestial, "Class2", iClass2); + SetLocalInt(oCelestial, "Class2Package", iClass2Package); + SetLocalInt(oCelestial, "Class2Start", iClass2Start); + SetLocalInt(oCelestial, "Class2Start", iClass2Start); + SetLocalInt(oCelestial, "iBaseCL", iBaseCL); + SetLocalInt(oCelestial, "X2_L_BEH_MAGIC", iMagicUse); + SetLocalString(oCelestial, "X2_SPECIAL_COMBAT_AI_SCRIPT", sAI); + + return oCelestial; +} + +//:: Adds Paragon SLA's to jCreature. +//:: +json json_AddParagonPowers(json jCreature) +{ + // Get the existing SpecAbilityList (if it exists) + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + + // Create the SpecAbilityList if it doesn't exist + if (jSpecAbilityList == JsonNull()) + { + jSpecAbilityList = JsonArray(); + } + +//:: Greater Dispelling 3x / Day + int i; + for (i = 0; i < 3; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 67); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: Add Haste 3x / Day + for (i = 0; i < 3; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 78); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: See Invisiblity 3x / Day + for (i = 0; i < 3; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 157); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: Add the list to the creature + jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); + + return jCreature; +} + +//:: Directly modifies jCreature's Challenge Rating. +//:: This is useful for most XP calculations. +//:: +json json_UpdateParagonCR(json jCreature, int nBaseCR, int nBaseHD) +{ + int nNewCR; + +//:: Calculate additional CR by HD + if(nBaseHD <= 6) + { + nNewCR = nBaseCR + 18; + } + else if(nBaseHD <= 16) + { + nNewCR = nBaseCR + 15; + } + else + {nNewCR = nBaseCR + 12;} + +//:: Modify Challenge Rating + jCreature = GffReplaceFloat(jCreature, "ChallengeRating"/* /value" */, IntToFloat(nNewCR)); + + return jCreature; +} + +//:: Adds Psuedonatural SLA's to jCreature. +//:: +json json_AddPsuedonaturalPowers(json jCreature) +{ + // Get the existing SpecAbilityList (if it exists) + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + + // Create the SpecAbilityList if it doesn't exist + if (jSpecAbilityList == JsonNull()) + { + jSpecAbilityList = JsonArray(); + } + +//:: True Strike 1x / Day + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 415); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: Add the list to the creature + jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); + + return jCreature; +} + +//:: Directly modifies jCreature's Challenge Rating. +//:: This is useful for most XP calculations. +//:: +json json_UpdatePsuedonaturalCR(json jCreature, int nBaseCR, int nBaseHD) +{ + int nNewCR; + +//:: Calculate additional CR by HD + if (nBaseHD >= 4 && nBaseHD <= 11) + { + nNewCR = nBaseCR + 1; + } + else if (nBaseHD >= 12) + { + nNewCR = nBaseCR + 2; + } + +//:: Modify Challenge Rating + jCreature = GffReplaceFloat(jCreature, "ChallengeRating"/* /value" */, IntToFloat(nNewCR)); + + return jCreature; +} + + +//:: Spawns a Psuedonatural creature from a template +object MakePsuedonaturalCreatureFromTemplate(string sResref, location lSpawnLoc) +{ + json jPsuedo = TemplateToJson(sResref, RESTYPE_UTC); + if (jPsuedo == JSON_NULL) + { + DoDebug("prc_inc_json >> SpawnPsuedonaturalCreatureFromTemplate: TemplateToJson failed — bad resref or resource missing."); + return OBJECT_INVALID; + } + + //:: Get current HD + int nCurrentHD = json_GetCreatureHD(jPsuedo); + if (nCurrentHD <= 0) + { + DoDebug("make_psuedonat >> MakePsuedonaturalCreatureFromTemplate failed — template missing HD data."); + return OBJECT_INVALID; + } + + //:: Get current CR + int nBaseCR = 1; + nBaseCR = json_GetCreatureHD(jPsuedo); + if (nBaseCR <= 0) + { + DoDebug("make_psuedonat >> MakePsuedonaturalCreatureFromTemplate failed — template missing CR data."); + return OBJECT_INVALID; + } + + //:: Get local vars to transfer over. + int iMinHD = json_GetLocalIntFromVarTable(jPsuedo, "iMinHD"); + int iMaxHD = json_GetLocalIntFromVarTable(jPsuedo, "iMaxHD"); + int nOriginalHD = json_GetLocalIntFromVarTable(jPsuedo, "nOriginalHD"); + int iClass2 = json_GetLocalIntFromVarTable(jPsuedo, "Class2"); + int iClass2Package = json_GetLocalIntFromVarTable(jPsuedo, "Class2Package"); + int iClass2Start = json_GetLocalIntFromVarTable(jPsuedo, "Class2Start"); + int iMagicUse = json_GetLocalIntFromVarTable(jPsuedo, "X2_L_BEH_MAGIC"); + string sAI = json_GetLocalStringFromVarTable(jPsuedo, "X2_SPECIAL_COMBAT_AI_SCRIPT"); + + //:: Adds True Strike 1x / day to jCreature. + jPsuedo = json_AddPsuedonaturalPowers(jPsuedo); + + //:: Change jCreature's racialtype to outsider + jPsuedo = json_ModifyRacialType(jPsuedo, RACIAL_TYPE_OUTSIDER); + + jPsuedo = json_UpdatePsuedonaturalCR(jPsuedo, nBaseCR, nCurrentHD); + + //:: Spawn the creature + object oPsuedo = JsonToObject(jPsuedo, lSpawnLoc); + + //:: Set variables + SetLocalInt(oPsuedo, "TEMPLATE_PSUEDONATURAL", 1); + SetLocalInt(oPsuedo, "iMinHD", iMinHD); + SetLocalInt(oPsuedo, "iMaxHD", iMaxHD); + SetLocalInt(oPsuedo, "nOriginalHD", nOriginalHD); + SetLocalInt(oPsuedo, "Class2", iClass2); + SetLocalInt(oPsuedo, "Class2Package", iClass2Package); + SetLocalInt(oPsuedo, "Class2Start", iClass2Start); + SetLocalInt(oPsuedo, "X2_L_BEH_MAGIC", iMagicUse); + SetLocalString(oPsuedo, "X2_SPECIAL_COMBAT_AI_SCRIPT", sAI); + + return oPsuedo; + +} //:: Test void -//:: void main (){} \ No newline at end of file +//::void main (){} \ No newline at end of file diff --git a/src/include/prc_inc_material.nss b/src/include/prc_inc_material.nss index dc050aa..bb84f86 100644 --- a/src/include/prc_inc_material.nss +++ b/src/include/prc_inc_material.nss @@ -430,8 +430,8 @@ string GetMaterialName( int iMaterialType, int bLowerCase = FALSE) case IP_MATERIAL_ROPE_GIANT_HAIR: sName = IP_MATERIAL_NAME_ROPE_GIANT_HAIR; break; case IP_MATERIAL_OBSIDIAN: sName = IP_MATERIAL_NAME_OBSIDIAN; break; case IP_MATERIAL_BAMBOO: sName = IP_MATERIAL_NAME_BAMBOO; break; - case IP_MATERIAL_POTTERY: sName = IP_MATERIAL_NAME_POTTERY; break; - case IP_MATERIAL_GLASSTEEL: sName = IP_MATERIAL_NAME_GLASSTEEL; break; + case IP_MATERIAL_POTTERY: sName = IP_MATERIAL_NAME_POTTERY; break; + case IP_MATERIAL_GLASSTEEL: sName = IP_MATERIAL_NAME_GLASSTEEL; break; case IP_MATERIAL_HERB: sName = IP_MATERIAL_NAME_HERB; break; default: return ""; diff --git a/src/include/prc_inc_nat_hb.nss b/src/include/prc_inc_nat_hb.nss index c8f7c27..10bc2fb 100644 --- a/src/include/prc_inc_nat_hb.nss +++ b/src/include/prc_inc_nat_hb.nss @@ -1,9 +1,68 @@ +//:: prc_inc_nat_hb +//:: +//:: void main(){} + + void DoNaturalWeaponHB(object oPC = OBJECT_SELF); #include "prc_inc_combat" #include "prc_inc_template" - -object GetProperTarget(object oPC, object oTarget) +/** + * Finds a valid enemy target in melee range when the original target is invalid. + * Now includes input validation, LOS checks, configurable radius, and target priority. + * + * @param oPC The creature seeking a new target + * @param oTarget The original (invalid) target + * @param fRadius Search radius in meters (optional, defaults to melee range) + * @return A valid enemy target or OBJECT_INVALID if none found + */ +object GetProperTarget(object oPC, object oTarget, float fRadius = MELEE_RANGE_METERS) +{ + // Input validation + if(!GetIsObjectValid(oPC)) + { + DoDebug("GetProperTarget(): Invalid oPC parameter"); + return OBJECT_INVALID; + } + + // Use target list system for better target selection + PurgeTargetList(oPC); + + location lPC = GetLocation(oPC); + object oTest = MyFirstObjectInShape(SHAPE_SPHERE, fRadius, lPC, TRUE, OBJECT_TYPE_CREATURE); + + while(GetIsObjectValid(oTest)) + { + // Basic validation checks + if(oTest != oPC && // Not self + GetIsEnemy(oPC, oTest) && // Is enemy + GetIsInMeleeRange(oPC, oTest) && // In melee range + !GetIsDead(oTest) && // Is alive + LineOfSightObject(oPC, oTest)) // Has line of sight + { + // Add to target list with priority based on distance (nearest first) + AddToTargetList(oTest, oPC, INSERTION_BIAS_DISTANCE, FALSE); + } + + oTest = MyNextObjectInShape(SHAPE_SPHERE, fRadius, lPC, TRUE, OBJECT_TYPE_CREATURE); + } + + // Get the highest priority target (nearest enemy) + object oBestTarget = GetTargetListHead(oPC); + PurgeTargetList(oPC); + + if(GetIsObjectValid(oBestTarget)) + { + DoDebug("GetProperTarget(): Selected target " + GetName(oBestTarget) + + " for " + GetName(oPC)); + return oBestTarget; + } + + // No valid target found + DoDebug("GetProperTarget(): No valid target found for " + GetName(oPC)); + return OBJECT_INVALID; +} +/* object GetProperTarget(object oPC, object oTarget) { location lTarget = GetLocation(oPC); // Use the function to get the closest creature as a target @@ -21,7 +80,7 @@ object GetProperTarget(object oPC, object oTarget) } return oTarget; -} +} */ void DoNaturalAttack(object oWeapon) { @@ -289,59 +348,72 @@ void DoOverflowOnhandAttack(int nAttackMod) ); } -void DoNaturalWeaponHB(object oPC = OBJECT_SELF) +/* void DoNaturalWeaponHB(object oPC = OBJECT_SELF) { //not in combat, abort if(!GetIsInCombat(oPC)) return; -// if(DEBUG) DoDebug("entered DoNaturalWeaponHB"); + if(DEBUG) DoDebug("prc_inc_nat_hb: entered DoNaturalWeaponHB"); float fDelay = 0.1 + IntToFloat(Random(10))/100.0; //no natural weapons, abort //in a different form, abort for now fix it later - if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) - && !GetIsPolyMorphedOrShifted(oPC)) +if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) + && !GetIsPolyMorphedOrShifted(oPC)) +{ + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: creature has natural secondary weapons"); + UpdateSecondaryWeaponSizes(oPC); + int i; + while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) { - // DoDebug("DoNaturalWeaponHB: creature has natural secondary weapons"); - UpdateSecondaryWeaponSizes(oPC); - int i; - while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) + string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); + if(sResRef != "") { - //get the resref to use - string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); - //if null, move to next - if(sResRef != "") + // Get stored weapon object, or create if doesn't exist + object oWeapon = GetLocalObject(oPC, "NAT_SEC_WEAP_" + sResRef); + + if(!GetIsObjectValid(oWeapon)) { - //get the created item - object oWeapon = GetObjectByTag(sResRef); + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: creating and storing creature weapon " + sResRef); + oWeapon = CreateItemOnObject(sResRef, oPC); + if(!GetIsObjectValid(oWeapon)) { - object oLimbo = GetObjectByTag("HEARTOFCHAOS"); - location lLimbo = GetLocation(oLimbo); - if(!GetIsObjectValid(oLimbo)) - lLimbo = GetStartingLocation(); - oWeapon = CreateObject(OBJECT_TYPE_ITEM, sResRef, lLimbo); + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: ERROR - CreateItemOnObject FAILED for " + sResRef); } + else + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: SUCCESS - weapon created, tag=" + GetTag(oWeapon) + ", name=" + GetName(oWeapon)); + SetIdentified(oWeapon, TRUE); + SetLocalObject(oPC, "NAT_SEC_WEAP_" + sResRef, oWeapon); + } + } + else + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: using stored creature weapon object"); + } - // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); - //do the attack within a delay - /* - // motu99: commented this out; AssignCommand ist not needed, because OBJECT_SELF is oPC - using AssignCommand will only degrade performance - AssignCommand(oPC, DelayCommand(fDelay, DoNaturalAttack(oWeapon))); - */ - + // Double-check validity before scheduling + if(GetIsObjectValid(oWeapon)) + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); DelayCommand(fDelay, DoNaturalAttack(oWeapon)); - //calculate the delay to use next time fDelay += 2.05; if(fDelay > 6.0) - fDelay -= 6.0; + fDelay -= 6.0; + } + else + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: ERROR - weapon object is INVALID, cannot schedule attack"); } - i++; } + i++; } +} + int iMod = 5; // motu99: added check for monk weapon if(GetHasMonkWeaponEquipped(oPC)) iMod = 3; @@ -357,10 +429,10 @@ void DoNaturalWeaponHB(object oPC = OBJECT_SELF) for(i = 0; i < nOverflowAttackCount; i++) { // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted overflow attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); - /* + // motu99: see comment above why this is commented out - AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); - */ + //AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); + DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty)); //calculate the delay to use @@ -399,6 +471,128 @@ void DoNaturalWeaponHB(object oPC = OBJECT_SELF) } } } + */ + +void DoNaturalWeaponHB(object oPC = OBJECT_SELF) +{ + //not in combat, abort + if(!GetIsInCombat(oPC)) + return; + + if(DEBUG) DoDebug("prc_inc_nat_hb: entered DoNaturalWeaponHB"); + + float fDelay = 0.1 + IntToFloat(Random(10))/100.0; + + //no natural weapons, abort + //in a different form, abort for now fix it later + if(array_exists(oPC, ARRAY_NAT_SEC_WEAP_RESREF) + && !GetIsPolyMorphedOrShifted(oPC)) + { + DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: creature has natural secondary weapons"); + UpdateSecondaryWeaponSizes(oPC); + int i; + while(i < array_get_size(oPC, ARRAY_NAT_SEC_WEAP_RESREF)) + { + //get the resref to use + string sResRef = array_get_string(oPC, ARRAY_NAT_SEC_WEAP_RESREF, i); + //if null, move to next + if(sResRef != "") + { + //get the created item + object oWeapon = GetObjectByTag(sResRef); + if(!GetIsObjectValid(oWeapon)) + { + object oLimbo = GetObjectByTag("HEARTOFCHAOS"); + location lLimbo = GetLocation(oLimbo); + if(!GetIsObjectValid(oLimbo)) + lLimbo = GetStartingLocation(); + oWeapon = CreateObject(OBJECT_TYPE_ITEM, sResRef, lLimbo); + DoDebug(PRC_TEXT_WHITE + "prc_inc_nat_hb >> DoNaturalWeaponHB: creature weapon object found!!!"); + } + + // Check for enhancements after creating the weapon object + int nEnhance = GetLocalInt(oPC, "PRC_NAT_WEAPON_ENHANCE"); + if(nEnhance > 0) + { + + DoDebug(PRC_TEXT_WHITE + "prc_inc_nat_hb >> DoNaturalWeaponHB: Applying enhancement."); + float fDuration = GetLocalFloat(oPC, "PRC_NAT_WEAPON_ENH_DUR"); + IPSafeAddItemProperty(oWeapon, ItemPropertyEnhancementBonus(nEnhance), fDuration, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, TRUE); + } + + if(DEBUG) DoDebug("prc_inc_nat_hb >> DoNaturalWeaponHB: scheduling a secondary natural attack with "+GetName(oWeapon)+" at delay "+FloatToString(fDelay)); + //do the attack within a delay + // motu99: commented this out; AssignCommand ist not needed, because OBJECT_SELF is oPC - using AssignCommand will only degrade performance + //AssignCommand(oPC, DelayCommand(fDelay, DoNaturalAttack(oWeapon))); + + DelayCommand(fDelay, DoNaturalAttack(oWeapon)); + + //calculate the delay to use next time + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + } + i++; + } + } + + int iMod = 5; // motu99: added check for monk weapon + if(GetHasMonkWeaponEquipped(oPC)) iMod = 3; + + // check for overflow (main hand) attacks + int nOverflowAttackCount = GetLocalInt(oPC, "OverflowBaseAttackCount"); + if(nOverflowAttackCount) + { + int i; + // the first overflow attack would be the seventh main hand attack, at an AB of -30 + int nAttackPenalty = -6 * iMod; // -30 for normal bab, -18 for monks + // DoDebug("DoNaturalWeaponHB(): number of scripted overflow attacks: "+IntToString(nOverflowAttackCount)); + for(i = 0; i < nOverflowAttackCount; i++) + { + // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted overflow attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); + + // motu99: see comment above why this is commented out + // AssignCommand(oPC, DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty))); + + DelayCommand(fDelay, DoOverflowOnhandAttack(nAttackPenalty)); + + //calculate the delay to use + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + //calculate new attack penalty + nAttackPenalty -= iMod; // motu99: usually -5, for monks -3 (unarmed or kama) + } + } + + // motu99: this is only here for debugging in order to test PerformAttackRound() + // must be deleted after debugging!!! + //if (GetPRCSwitch(PRC_PNP_TRUESEEING)) DelayCommand(0.01, DoOffhandAttackRound()); + + + // check for overflow offhand attacks + int nOffhandAttackCount = GetLocalInt(oPC, "OffhandOverflowAttackCount"); +// if (DEBUG) DoDebug("DoNaturalWeaponHB: number of scripted offhand attacks = "+IntToString(nOffhandAttackCount)); + if(nOffhandAttackCount) + { + int i; + int nAttackPenalty = -2 * iMod; // offhand attacks always come at -5 per additional attack (but for monks we assume -3) + for(i = 0; i < nOffhandAttackCount; i++) + { + // DoDebug(COLOR_WHITE + "DoNaturalWeaponHB(): scheduling a scripted offhand attack with attack mod "+IntToString(nAttackPenalty)+" at delay "+FloatToString(fDelay)); + + DelayCommand(fDelay, DoOffhandAttack(nAttackPenalty)); + + //calculate the delay to use + fDelay += 2.05; + if(fDelay > 6.0) + fDelay -= 6.0; + //calculate new attack penalty + nAttackPenalty -= iMod; + } + } +} + /* * motu99's test functions. Not actually used by PRC scripts diff --git a/src/include/prc_inc_natweap.nss b/src/include/prc_inc_natweap.nss index 8d3668b..78115a5 100644 --- a/src/include/prc_inc_natweap.nss +++ b/src/include/prc_inc_natweap.nss @@ -277,6 +277,32 @@ void ClearNaturalWeapons(object oPC) array_delete(oPC, ARRAY_NAT_PRI_WEAP_ATTACKS); } +/** + * @brief Adds a natural primary weapon to a creature (PC/NPC). + * + * This function manages a creature's natural primary weapons by storing their + * resource references and attack counts in persistent arrays. If the weapon + * being added is the first natural weapon, it may automatically become the + * creature's active primary natural weapon, unless the creature is a Monk or + * Brawler. Optionally, the weapon can be forced to become the active primary + * weapon regardless of class. + * + * @param oPC The creature object to which the natural weapon will be added. + * @param sResRef The resource reference string of the natural weapon. + * @param nCount (Optional) The number of attacks this natural weapon provides. + * Default is 1. + * @param nForceUse (Optional) If TRUE, forces this weapon to become the active + * primary natural weapon regardless of the creature's class. + * Default is FALSE. + * + * @details + * - Creates persistent arrays for weapon references and attack counts if they + * do not already exist. + * - Checks if the weapon is already present to avoid duplicates. + * - Adds the weapon and attack count to the arrays. + * - Sets the primary natural weapon index to this weapon if it is the first + * natural weapon added, unless the creature is a Monk or Brawler. + */ void AddNaturalPrimaryWeapon(object oPC, string sResRef, int nCount = 1, int nForceUse = FALSE) { int nFirstNaturalWeapon = FALSE; diff --git a/src/include/prc_inc_nwscript.nss b/src/include/prc_inc_nwscript.nss index 8ebc821..4671756 100644 --- a/src/include/prc_inc_nwscript.nss +++ b/src/include/prc_inc_nwscript.nss @@ -572,7 +572,10 @@ int GetMaxEssentiaCapacity(object oMeldshaper, int nClass, int nMeld) { int nMax = 1; // Always can invest one int nHD = GetHitDice(oMeldshaper); - if (nHD >= 31) nMax = 5; + if (nHD >= 61) nMax = 8; + else if (nHD >= 51) nMax = 7; + else if (nHD >= 41) nMax = 6; + else if (nHD >= 31) nMax = 5; else if (nHD >= 18) nMax = 4; else if (nHD >= 12) nMax = 3; else if (nHD >= 6) nMax = 2; diff --git a/src/include/prc_inc_onhit.nss b/src/include/prc_inc_onhit.nss index 3f0da67..ab68887 100644 --- a/src/include/prc_inc_onhit.nss +++ b/src/include/prc_inc_onhit.nss @@ -808,7 +808,24 @@ int GetIsOnHitCastSpell(object oSpellTarget = OBJECT_INVALID, object oSpellCastI if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is armor; attacker = "+GetName(oAttacker)+", defender = "+GetName(oDefender)); } // is the spell type item a weapon? - else if (iWeaponType == StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType))) + int nWT = StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType)); + if (nWT > 0) + { + if (oSpellTarget == OBJECT_INVALID) + oSpellTarget = PRCGetSpellTargetObject(oSpellOrigin); + + oAttacker = oSpellOrigin; + oDefender = oSpellTarget; + + if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is weapon [WT="+IntToString(nWT)+"]; attacker="+GetName(oAttacker)+", defender="+GetName(oDefender)); + } + else + { + if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is neither weapon nor armor; returning FALSE"); + return FALSE; + } + +/* else if (iWeaponType == StringToInt(Get2DACache("baseitems", "WeaponType", iBaseType))) { // determine the target, if not already given if (oSpellTarget == OBJECT_INVALID) @@ -823,7 +840,7 @@ int GetIsOnHitCastSpell(object oSpellTarget = OBJECT_INVALID, object oSpellCastI { if (DEBUG) DoDebug("GetIsOnHitCastSpell: item "+GetName(oSpellCastItem)+" is neither weapon nor armor; returning FALSE"); return FALSE; - } + } */ // the spell origin must possess the item that cast the spell (at least for the aurora engine, in prc_inc_combat that may differ) diff --git a/src/include/prc_inc_shifting.nss b/src/include/prc_inc_shifting.nss index 7cac1ed..1e0fadc 100644 --- a/src/include/prc_inc_shifting.nss +++ b/src/include/prc_inc_shifting.nss @@ -1370,7 +1370,9 @@ void _prc_inc_shifting_ShiftIntoTemplateAux(object oShifter, int nShifterType, o if(GetIsObjectValid(oShifterCWpR)) MyDestroyObject(oShifterCWpR); if(GetIsObjectValid(oShifterCWpL)) MyDestroyObject(oShifterCWpL); if(GetIsObjectValid(oShifterCWpB)) MyDestroyObject(oShifterCWpB); - oShifterCWpR = oShifterCWpL = oShifterCWpR = OBJECT_INVALID; + oShifterCWpR = OBJECT_INVALID; + oShifterCWpL = OBJECT_INVALID; + oShifterCWpB = OBJECT_INVALID; // Copy the template's weapons and assign equipping diff --git a/src/include/prc_inc_size.nss b/src/include/prc_inc_size.nss new file mode 100644 index 0000000..286015b --- /dev/null +++ b/src/include/prc_inc_size.nss @@ -0,0 +1,154 @@ +#include "prc_inc_util" +#include "prc_inc_spells" +#include "prc_inc_function" + +// Wrapper function for delayed visual transform with generation tracking +void DelayedSetVisualTransform(int nExpectedGeneration, object oTarget, int nTransform, float fValue) +{ + // Read current generation at execution time, not when scheduled + if (nExpectedGeneration != GetLocalInt(oTarget, "PRC_SIZE_GENERATION")) + { + // Generation has changed, don't apply the transform + return; + } + SetObjectVisualTransform(oTarget, nTransform, fValue); +} + +// Main wrapper function that handles generation tracking +void DelaySetVisualTransform(float fDelay, object oTarget, string sGenerationName, int nTransform, float fValue) +{ + int nExpectedGeneration = GetLocalInt(oTarget, sGenerationName); + DelayCommand(fDelay, DelayedSetVisualTransform(nExpectedGeneration, oTarget, nTransform, fValue)); +} + +/** + * Creates a size change effect that can enlarge or reduce a creature + * + * @param oTarget Object to affect + * @param nObjectType Object type filter (OBJECT_TYPE_CREATURE, etc.) + * @param bEnlarge TRUE to enlarge, FALSE to reduce + * @param nChanges Number of size categories to change (0 = reset to original) + * @return The size change effect + */ + +effect EffectSizeChange(object oTarget, int nObjectType, int bEnlarge, int nChanges) +{ + effect eBlank; + + // Increment generation for any size change + int nGeneration = PRC_NextGeneration(GetLocalInt(oTarget, "PRC_SIZE_GENERATION")); + SetLocalInt(oTarget, "PRC_SIZE_GENERATION", nGeneration); + + // Store original size if not already stored - READ ACTUAL CURRENT SCALE + if(GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE") == 0.0f) + { + float fCurrentScale = GetObjectVisualTransform(oTarget, OBJECT_VISUAL_TRANSFORM_SCALE); + SetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE", fCurrentScale); + } + + // Reset to original size + if(nChanges == 0) + { + float fOriginalSize = GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fOriginalSize); + // DON'T delete PRC_ORIGINAL_SIZE here - keep it for future casts + return eBlank; + } + + // Get the original scale + float fOriginalScale = GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + + // Calculate scale factor relative to original size + float fScale = fOriginalScale; + if(bEnlarge) + fScale = fOriginalScale * pow(1.5f, IntToFloat(nChanges)); + else + fScale = fOriginalScale * pow(0.5f, IntToFloat(nChanges)); + + // Create the effect link with sanctuary VFX + effect eReturn = EffectLinkEffects(EffectVisualEffect(VFX_DUR_SANCTUARY), + EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE)); + + if(bEnlarge) + { + eReturn = EffectLinkEffects(eReturn, EffectAttackDecrease(nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityIncrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACDecrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_INCREASE"); + } + else + { + eReturn = EffectLinkEffects(eReturn, EffectAttackIncrease(nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityIncrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACIncrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_DECREASE"); + } + + // Apply visual transform using wrapper + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fScale); + + return eReturn; +} + + + + +/* effect EffectSizeChange(object oTarget, int nObjectType, int bEnlarge, int nChanges) +{ + effect eBlank; + + // Increment generation for any size change + int nGeneration = PRC_NextGeneration(GetLocalInt(oTarget, "PRC_SIZE_GENERATION")); + SetLocalInt(oTarget, "PRC_SIZE_GENERATION", nGeneration); + + // Store original size if not already stored (fixed check) + if(GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE") == 0.0f) + { + SetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE", 1.0f); + } + + // Reset to original size + if(nChanges == 0) + { + float fOriginalSize = GetLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fOriginalSize); + DeleteLocalFloat(oTarget, "PRC_ORIGINAL_SIZE"); + return eBlank; + } + + // Calculate scale factor using pow() for multi-step changes + float fScale = 1.0f; + if(bEnlarge) + fScale = pow(1.5f, IntToFloat(nChanges)); // 1.5, 2.25, 3.375... + else + fScale = pow(0.5f, IntToFloat(nChanges)); // 0.5, 0.25, 0.125... + + // Create the effect link based on enlarge/reduce + effect eReturn; + if(bEnlarge) + { + eReturn = EffectLinkEffects(EffectAttackDecrease(nChanges), + EffectAbilityDecrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityIncrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACDecrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_INCREASE"); + } + else + { + eReturn = EffectLinkEffects(EffectAttackIncrease(nChanges), + EffectAbilityIncrease(ABILITY_DEXTERITY, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectAbilityDecrease(ABILITY_STRENGTH, 2 * nChanges)); + eReturn = EffectLinkEffects(eReturn, EffectACIncrease(nChanges)); + eReturn = TagEffect(eReturn, "PRC_SIZE_DECREASE"); + } + + // Apply visual transform using wrapper + DelaySetVisualTransform(0.0f, oTarget, "PRC_SIZE_GENERATION", OBJECT_VISUAL_TRANSFORM_SCALE, fScale); + + return eReturn; +} + */ + +//:: void main(){} \ No newline at end of file diff --git a/src/include/prc_inc_skills.nss b/src/include/prc_inc_skills.nss index 0674604..1bfc354 100644 --- a/src/include/prc_inc_skills.nss +++ b/src/include/prc_inc_skills.nss @@ -363,8 +363,11 @@ int PRCIsFlying(object oCreature) bFlying = TRUE; } if(!bFlying - && ((nWings > 0 && nWings < 79) || nWings == 90))//CEP and Project Q wing models - bFlying = TRUE; + && ((nWings > 0 && nWings < 79) + || (nWings > 1959 && nWings < 1962) + || (nWings > 1962 && nWings < 1966) + || nWings == 90))//CEP and Project Q wing models + bFlying = TRUE; if (GetHasSpellEffect(MOVE_SH_BALANCE_SKY, oCreature)) bFlying = TRUE; diff --git a/src/include/prc_inc_spells.nss b/src/include/prc_inc_spells.nss index 18c0efa..1c7ad71 100644 --- a/src/include/prc_inc_spells.nss +++ b/src/include/prc_inc_spells.nss @@ -1011,11 +1011,16 @@ int PRCMySavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = // Plague Resistant gives a +4 bonus on disease saves if(GetHasFeat(FEAT_PLAGUE_RESISTANT, oTarget)) nDC -= 4; + // Racial +2 vs disease saves + if(GetHasFeat(FEAT_RACE_HARDINESS_VS_DISEASE, oTarget)) + nDC -= 2; // +4/+2 bonus on saves against disease, done here if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13) nDC -= 4; else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3) nDC -= 2; + + } else if(nSaveType == SAVING_THROW_TYPE_POISON) { diff --git a/src/include/prc_inc_switch.nss b/src/include/prc_inc_switch.nss index b2c7160..e2a3dfe 100644 --- a/src/include/prc_inc_switch.nss +++ b/src/include/prc_inc_switch.nss @@ -70,13 +70,13 @@ 43 PRC_CRAFTING_BASE_ITEMS int 1 44 PRC_XP_USE_SIMPLE_LA int 1 45 PRC_XP_USE_SIMPLE_RACIAL_HD int 1 -46 PRC_CREATE_INFUSION_CASTER_LEVEL int 1 -47 PRC_CREATE_INFUSION_OPTIONAL_HERBS int 0 +46 PRC_CREATE_INFUSION_CASTER_LEVEL int 1 +47 PRC_CREATE_INFUSION_OPTIONAL_HERBS int 0 */ /* This variable MUST be updated with every new version of the PRC!!! */ - const string PRC_VERSION = "PRC 3.9.0"; + const string PRC_VERSION = "PRC8 4.76"; /* This variable MUST be updated every time 'assemble_spellbooks.bat' is run!!! */ @@ -91,11 +91,11 @@ * This allows material components in NWN through the materialcomp.2da * Just put the SpellID and UTC resref in, MINUS the .utc on the end. * This also requires the names of the items, formatted like so ("" included): "Object Name" - * + * * Set switch to 2 to activate this * Deducts gold instead of requiring material components * Put the gold value in the Cost column - + * Set switch to 3 to activate both at the same time * WARNING: This will slow spellcasting down due to 2da reads and inventory loops */ @@ -129,7 +129,7 @@ const string PRC_BIOWARE_NEUTRALIZE_POISON = "PRC_BIOWARE_NEUTRALIZE_P /** Remove the cap PRC added to this spell */ const string PRC_BIOWARE_REMOVE_DISEASE = "PRC_BIOWARE_REMOVE_DISEASE"; -/** +/** * This replaces the 3.0 Spell Focus bonuses with the 3.5 edition ones */ const string PRC_35_SPELL_FOCUS = "PRC_35_SPELL_FOCUS"; @@ -263,8 +263,8 @@ const string PRC_165_DEATH_IMMUNITY = "PRC_165_DEATH_IMMUNITY"; /* * PRC_ACTIVATE_MAX_SPELL_DC_CAP: activate a max cap on DC casted by creature/player - * PRC_SET_MAX_SPELL_DC_CAP: the max value ex: 99 - * + * PRC_SET_MAX_SPELL_DC_CAP: the max value ex: 99 + * */ const string PRC_ACTIVATE_MAX_SPELL_DC_CAP = "PRC_ACTIVATE_MAX_SPELL_DC_CAP"; const string PRC_SET_MAX_SPELL_DC_CAP = "PRC_SET_MAX_SPELL_DC_CAP"; @@ -291,8 +291,8 @@ const string PRC_DC_BASE_OVERRIDE = "PRC_DC_BASE_OVERRIDE"; const string PRC_DC_ADJUSTMENT = "PRC_DC_ADJUSTMENT"; /* - * By default when calculating caster level for characters with PrCs, the highest class rule will - * be used (ie. Bard 2/Wizard 4/Elemental Savant 6 - Wizard is the highest arcane class so levels + * By default when calculating caster level for characters with PrCs, the highest class rule will + * be used (ie. Bard 2/Wizard 4/Elemental Savant 6 - Wizard is the highest arcane class so levels * form PrC will be added to that class, and the caster level will be 2 for Bard and 10 for Wizard). * When this is set, the first class rule will be used (with the same character caster level would * be 8 for Bard and 4 for Wizard). @@ -572,12 +572,12 @@ const string PRC_SOUL_EATER_MAX_SLAVES = "PRC_SOUL_EATER_MAX_SLAVES"; * For the Psionic Slayer prestige class, this switch limits the Favored Enemy selection * to the Aberration racial type. * - * This switch is provided to allow builders to more closely represent the Pen and Paper + * This switch is provided to allow builders to more closely represent the Pen and Paper * Illithid Slayer class, instead of the broader Open Game License "Slayer" class. * - * Type: Int + * Type: Int * Values: 0 [Default] (Favored Enemy racial type is not limited) - * 1 (Favored Enemy race is limited to Aberration only) + * 1 (Favored Enemy race is limited to Aberration only) */ const string PRC_PSIONIC_SLAYER_FAV_ENEMY_ABERRATION_ONLY = "PRC_PSIONIC_SLAYER_FAV_ENEMY_ABERRATION_ONLY"; @@ -585,20 +585,20 @@ const string PRC_PSIONIC_SLAYER_FAV_ENEMY_ABERRATION_ONLY = "PRC_PSIONIC_S * For the Psionic Slayer prestige class, this switch requires a character to make a "kill" * of a specific type of creature before the class becomes available. * - * Use of this switch requires that the module builder add a "Psionic Slayer Kill Token" - * (included in the PRC Items) to the designated creature. + * Use of this switch requires that the module builder add a "Psionic Slayer Kill Token" + * (included in the PRC Items) to the designated creature. * - * Alternately, a script or item can be made that will run the script "prc_psysly_killt" + * Alternately, a script or item can be made that will run the script "prc_psysly_killt" * on the PC. This script will set the flag that allows the target PC to take the Psionic Slayer Class. - * Example code: - * ExecuteScript("prc_psysly_killt", oPC); // Where oPC is an player charcter object + * Example code: + * ExecuteScript("prc_psysly_killt", oPC); // Where oPC is an player charcter object * - * This switch is provided to allow builders to more closely represent the Pen and Paper + * This switch is provided to allow builders to more closely represent the Pen and Paper * Illithid Slayer class, instead of the broader Open Game License "Slayer" class. * - * Type: Int + * Type: Int * Values: 0 [Default] (Kill Token / Script NOT required for taking the Psionic Slayer Class) - * 1 (Kill Token / Script REQUIRED before the Psionic Slayer Class is available to take) + * 1 (Kill Token / Script REQUIRED before the Psionic Slayer Class is available to take) */ const string PRC_PSIONIC_SLAYER_REQUIRE_KILL_TOKEN = "PRC_PSIONIC_SLAYER_REQUIRE_KILL_TOKEN"; @@ -607,19 +607,19 @@ const string PRC_PSIONIC_SLAYER_REQUIRE_KILL_TOKEN = "PRC_PSIONIC_SLAYER_R * By default the Werewolf class uses the Bioware Polymorph effect to perfrom its * Hybrid Form Shapchange. * - * This switch allows the Werewolf class to be toggled to use the PRC Shifter - * Shapchange code instead. + * This switch allows the Werewolf class to be toggled to use the PRC Shifter + * Shapchange code instead. * - * Type: Int + * Type: Int * Values: 0 [Default] (Werewolf Hybrid Shapchange uses Bioware Polymorph) - * 1 (Werewolf Hybrid Shapchange uses PRC Shifter shape change code) + * 1 (Werewolf Hybrid Shapchange uses PRC Shifter shape change code) */ -const string PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPCHANGE = "PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPCHANGE"; +const string PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPECHANGE = "PRC_WEREWOLF_HYBRID_USE_SHIFTER_SHAPECHANGE"; /** * Sets the max bonus for the PnP Shifter shifting systems * - * Type: Int + * Type: Int * Values: any greater than 0 */ const string PRC_PNP_SHIFTER_BONUS = "PRC_PNP_SHIFTER_BONUS"; @@ -783,7 +783,7 @@ const string PRC_STAFF_CASTER_LEVEL = "PRC_STAFF_CASTER_LEVEL"; /** * [DEFUNCT] * A wand must be equipped before it can cast a spell - * + * * Any value above 0 turns off the requirement to have a wand equipped to use it * * This switch is defunct, wands must *always* be equipped to use them. @@ -931,7 +931,7 @@ const string PRC_PNP_FAMILIAR_FEEDING = "PRC_PNP_FAMILIAR_FEEDING /** * Use PRC henchmen-familiars instead of BioWare's - this will allow - * new classes to have familiars, but summoned creatures will no longer + * new classes to have familiars, but summoned creatures will no longer * be 'true' familiars (ie. can't possess PRC familiar) */ const string PRC_FAMILIARS = "PRC_FAMILIARS"; @@ -1114,7 +1114,7 @@ const string PRC_SPELL_ALIGNMENT_RESTRICT = "PRC_SPELL_ALIGNMENT_REST * Disable registration of custom cohorts */ const string PRC_DISABLE_REGISTER_COHORTS = "PRC_DISABLE_REGISTER_COHORTS"; - + /* * Disable cohorts starting with gear */ @@ -1144,19 +1144,19 @@ const string PRC_SPELL_ALIGNMENT_RESTRICT = "PRC_SPELL_ALIGNMENT_REST * Medium armor is a 25% speed reduction, Heavy is a 33% reduction */ const string PRC_PNP_ARMOR_SPEED = "PRC_PNP_ARMOR_SPEED"; - + /* * Applies a 99% speed boost when out of combat * Warning that it will likely cause PCs to be overly speedy when combat starts * Potential problem causer */ const string PRC_FAST_TRAVEL_SPEED = "PRC_FAST_TRAVEL_SPEED"; - + /* * Applys a Discipline bonus equal to BAB to all characters if turned on * Bonus only applies to characters with 0 ranks in Discipline */ - const string PRC_PNP_KNOCKDOWN = "PRC_PNP_KNOCKDOWN"; + const string PRC_PNP_KNOCKDOWN = "PRC_PNP_KNOCKDOWN"; /* * by Bioware rules, PCs have approximatly a 7th faster movement than NPCs @@ -1204,8 +1204,11 @@ const string PRC_SPELL_ALIGNMENT_RESTRICT = "PRC_SPELL_ALIGNMENT_REST */ const string PRC_APPEARNCE_CHANGE_DISABLE = "PRC_APPEARNCE_CHANGE_DISABLE"; - - +/* + * Allow "Monk" gloves to merge with a creature weapons when Wildshaped. + * Will also merge bracers with creature hides when Wildshaped. +*/ +const string PRC_WILDSHAPE_ALLOWS_ARMS_SLOT = "PRC_WILDSHAPE_ALLOWS_ARMS_SLOT"; /******************************************************************************\ * Death/Bleeding system * @@ -1223,7 +1226,7 @@ const string PRC_PNP_DEATH_ENABLE = "PRC_PNP_DEATH_ENA * if FALSE, dont bleed just die * By PnP this would be 1 round, or 6 seconds */ -const string PRC_DEATH_OR_BLEED = "PRC_DEATH_OR_BLEED"; +const string PRC_DEATH_OR_BLEED = "PRC_DEATH_OR_BLEED"; /* * Damage when bleeding @@ -1316,7 +1319,7 @@ const string PRC_ACP_DELAY = "PRC_ACP_DELAY"; /****************************************************************************** -* File End switches +* File End switches ******************************************************************************/ /** @@ -1490,10 +1493,10 @@ const string PRC_POISON_IS_FOOD_SCRIPT_NAME = "PRC_POISON_IS_FOOD_SCRIP const string PRC_POISON_ALLOW_CLEAN_IN_EQUIP = "PRC_POISON_ALLOW_CLEAN_IN_EQUIP"; /** - * + * * Default: crafting requires only gold and xp */ -const string PRC_CRAFT_POISON_USE_INGREDIENST = "PRC_CRAFT_POISON_USE_INGREDIENST"; +const string PRC_CRAFT_POISON_USE_INGREDIENTS = "PRC_CRAFT_POISON_USE_INGREDIENTS"; /******************************************************************************\ * PRGT system switches * @@ -1542,10 +1545,10 @@ const string PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD = "PRC_PSI_ASTRAL_CONSTRUCT /** * If this is set, The Astral Seed power will attempt to use the provided string as - * the ResRef to create the Astral Seed object instead of the of the phylactery + * the ResRef to create the Astral Seed object instead of the of the phylactery * ResRef("x2_plc_phylact"). * May be used by builders to create an object that CAN be destroyed, or has other traits, - * as desired. + * as desired. * Type: String * Values: "" [Default] (Blank, or not set: Use default phylactery ResRef for Astral Seed) * STRING (Entered String will be used as the ResRef of created Astral Seed object) @@ -1553,14 +1556,14 @@ const string PRC_PSI_ASTRAL_CONSTRUCT_DUR_MOD = "PRC_PSI_ASTRAL_CONSTRUCT const string PRC_PSI_ASTRAL_SEED_RESREF = "PRC_PSI_ASTRAL_SEED_RESREF"; /** - * By default the Astral Seed power respawns the player, and then makes them immobile for + * By default the Astral Seed power respawns the player, and then makes them immobile for * 24-game-hours. - * If this switch is set, it will adjust the imobility time period; shortening it, lengthing it, or + * If this switch is set, it will adjust the imobility time period; shortening it, lengthing it, or * effectively eliminating it. * Type: Int * Values: 0 [Default] (Not set: Use default 24 hour duration) * -1 (Any negative value will result in a fixed duratoion of 2 seconds, which effectively eliminates the wait period) - * 1 (Any potitive value: multiply duration by the value provided and then divide result by 1000. + * 1 (Any potitive value: multiply duration by the value provided and then divide result by 1000. * Values less than 1000 will shorten the duration, values higher than 1000 will lengthen it.) */ const string PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000 = "PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000"; @@ -1571,7 +1574,7 @@ const string PRC_PSI_ASTRAL_SEED_RESPAWN_DELAY_X1000 = "PRC_PSI_ASTRAL_SE * If this flag is set, the XP loss is completely eliminated. The standard PRC event hook script * of "prc_pw_astralseed" may be used to script any additional effects to occure upon Astral Seed * respawning, including scripting specific XP loss amount. - * Type: Int + * Type: Int * Values: 0 [Default] (Not set: lose 1 level worth of XP upon Astral Seed respawn) * 1 (Any potitive value: Remove all XP loss from Astral Seed respawn) */ @@ -1908,7 +1911,7 @@ const string PRC_CRAFT_TIMER_MAX = "PRC_CRAFT_TIMER_MAX"; */ const string PRC_CRAFT_TIMER_MIN = "PRC_CRAFT_TIMER_MIN"; -/** +/* * These three switches modify Bioware crafting so that the items produced have the * casterlevel of the spellcaster who created them. Normally under Bioware, it is possible * for a level 3 caster to produce level 9 items and for a level 40 caster to only produce @@ -1955,16 +1958,21 @@ const string PRC_CRAFT_ROD_CASTER_LEVEL = "PRC_CRAFT_ROD_CASTER_LEVE const string PRC_CRAFT_STAFF_CASTER_LEVEL = "PRC_CRAFT_STAFF_CASTER_LEVEL"; /* - * As above, except it applies to herbal infusions + * As above, except it applies to scepters */ -const string PRC_CREATE_INFUSION_CASTER_LEVEL = "PRC_CREATE_INFUSION_CASTER_LEVEL"; +const string PRC_CRAFT_SCEPTER_CASTER_LEVEL = "PRC_CRAFT_SCEPTER_CASTER_LEVEL"; /* - * Builder's Option: Enables the optional PnP herbs for creating infusions. + * As above, except it applies to herbal infusions + */ +const string PRC_CREATE_INFUSION_CASTER_LEVEL = "PRC_CREATE_INFUSION_CASTER_LEVEL"; + +/* + * Builder's Option: Enables the optional PnP herbs for creating infusions. * Each herb is keyed to a spell circle level & spell school as shown on pg. 33 * of the Master's of the Wild sourcebook. */ -const string PRC_CREATE_INFUSION_OPTIONAL_HERBS = "PRC_CREATE_INFUSION_OPTIONAL_HERBS"; +const string PRC_CREATE_INFUSION_OPTIONAL_HERBS = "PRC_CREATE_INFUSION_OPTIONAL_HERBS"; /* * Characters with a crafting feat always have the appropriate base item in their inventory @@ -2015,6 +2023,13 @@ const string PRC_X2_CRAFTWAND_COSTMODIFIER = "PRC_X2_CRAFTWAND_COSTMO */ const string PRC_X2_CRAFTROD_COSTMODIFIER = "PRC_X2_CRAFTROD_COSTMODIFIER"; +/* + * cost modifier of spells crafted into scepters + * note that adding a second spell costs 75% + * defaults to 750 + */ +const string PRC_X2_CRAFTSCEPTER_COSTMODIFIER = "PRC_X2_CRAFTSCEPTER_COSTMODIFIER"; + /* * cost modifier of spells crafted into staffs * note that adding a second spell costs 75% and 3 or more costs 50% @@ -2342,7 +2357,7 @@ const string PRC_XP_GIVE_XP_TO_NPCS = "PRC_XP_GIVE_XP_TO_NPCS"; /** * Setting this switch will turn off the messages about being too far awy to gain XP */ -const string PRC_XP_DISABLE_SPAM = "PRC_XP_DISABLE_SPAM"; +const string PRC_XP_DISABLE_SPAM = "PRC_XP_DISABLE_SPAM"; /** * PCs must be in the same area as the CR to gain XP. @@ -2901,13 +2916,13 @@ const string PRC_PERFECTED_MAP_MULTIPLIER = "PRC_PERFECTED_MAP_MULTIP \******************************************************************************/ /** - * Sets how many seconds it takes to contact a vestige. + * Sets how many seconds it takes to contact a vestige. * Any number less than 6 is ignored */ const string PRC_CONTACT_VESTIGE_TIMER = "PRC_CONTACT_VESTIGE_TIMER"; /** - * Sets how many seconds it takes to bind a vestige. + * Sets how many seconds it takes to bind a vestige. * Any number less than 12 is ignored */ const string PRC_BIND_VESTIGE_TIMER = "PRC_BIND_VESTIGE_TIMER"; @@ -2941,6 +2956,20 @@ const string PRC_PW_SECURITY_CD_CHECK = "PRC_PW_SECURITY_CD_CHECK"; */ const string PRC_DEBUG = "PRC_DEBUG"; +/******************************************************************************\ +* Duration NUI Switches * +\******************************************************************************/ + +/** + * Toggles allowing player to remove friendly PC spells on player through Duration NUI + * instead of just their own spells. + */ +const string PRC_ALLOWED_TO_REMOVE_FRIENDLY_SPELLS = "PRC_ALLOWED_TO_REMOVE_FRIENDLY_SPELLS"; +/** + * Toggles allowing players to see the duration of hostile spells on them. + */ +const string PRC_ALLOWED_TO_SEE_HOSTILE_SPELLS = "PRC_ALLOWED_TO_SEE_HOSTILE_SPELLS"; + diff --git a/src/include/prc_inc_turning.nss b/src/include/prc_inc_turning.nss index 6c819d4..2b6852f 100644 --- a/src/include/prc_inc_turning.nss +++ b/src/include/prc_inc_turning.nss @@ -10,6 +10,8 @@ //::////////////////////////////////////////////// //::////////////////////////////////////////////// +#include "prc_spell_const" + ////////////////////////////////////////////////// /* Function prototypes */ ////////////////////////////////////////////////// diff --git a/src/include/prc_inc_unarmed.nss b/src/include/prc_inc_unarmed.nss index a08f912..27c3245 100644 --- a/src/include/prc_inc_unarmed.nss +++ b/src/include/prc_inc_unarmed.nss @@ -91,6 +91,107 @@ float DamageAvg(int iDamage); /* Function defintions */ ////////////////////////////////////////////////// +// StepDie: increases a damage die by 'nSteps' steps according to d20 SRD progression +// Increment the unarmed damage by nSteps +int StepDie(int nDamage, int nSteps) +{ + int i; + for (i = 0; i < nSteps; i++) + { + switch (nDamage) + { + // 1-dice increments + case IP_CONST_MONSTERDAMAGE_1d2: nDamage = IP_CONST_MONSTERDAMAGE_1d3; break; + case IP_CONST_MONSTERDAMAGE_1d3: nDamage = IP_CONST_MONSTERDAMAGE_1d4; break; + case IP_CONST_MONSTERDAMAGE_1d4: nDamage = IP_CONST_MONSTERDAMAGE_1d6; break; + case IP_CONST_MONSTERDAMAGE_1d6: nDamage = IP_CONST_MONSTERDAMAGE_1d8; break; + case IP_CONST_MONSTERDAMAGE_1d8: nDamage = IP_CONST_MONSTERDAMAGE_1d10; break; + case IP_CONST_MONSTERDAMAGE_1d10: nDamage = IP_CONST_MONSTERDAMAGE_1d12; break; + case IP_CONST_MONSTERDAMAGE_1d12: nDamage = IP_CONST_MONSTERDAMAGE_2d8; break; + + // 2-dice increments + //case IP_CONST_MONSTERDAMAGE_2d3: nDamage = IP_CONST_MONSTERDAMAGE_2d4; break; + case IP_CONST_MONSTERDAMAGE_2d4: nDamage = IP_CONST_MONSTERDAMAGE_2d6; break; + case IP_CONST_MONSTERDAMAGE_2d6: nDamage = IP_CONST_MONSTERDAMAGE_2d8; break; + case IP_CONST_MONSTERDAMAGE_2d8: nDamage = IP_CONST_MONSTERDAMAGE_2d10; break; + case IP_CONST_MONSTERDAMAGE_2d10: nDamage = IP_CONST_MONSTERDAMAGE_2d12; break; + case IP_CONST_MONSTERDAMAGE_2d12: nDamage = IP_CONST_MONSTERDAMAGE_3d10; break; + + // 3-dice increments + case IP_CONST_MONSTERDAMAGE_3d4: nDamage = IP_CONST_MONSTERDAMAGE_3d6; break; + case IP_CONST_MONSTERDAMAGE_3d6: nDamage = IP_CONST_MONSTERDAMAGE_3d8; break; + case IP_CONST_MONSTERDAMAGE_3d8: nDamage = IP_CONST_MONSTERDAMAGE_3d10; break; + case IP_CONST_MONSTERDAMAGE_3d10: nDamage = IP_CONST_MONSTERDAMAGE_3d12; break; + case IP_CONST_MONSTERDAMAGE_3d12: nDamage = IP_CONST_MONSTERDAMAGE_4d8; break; + + // 4-dice increments + case IP_CONST_MONSTERDAMAGE_4d4: nDamage = IP_CONST_MONSTERDAMAGE_4d6; break; + case IP_CONST_MONSTERDAMAGE_4d6: nDamage = IP_CONST_MONSTERDAMAGE_4d8; break; + case IP_CONST_MONSTERDAMAGE_4d8: nDamage = IP_CONST_MONSTERDAMAGE_4d10; break; + case IP_CONST_MONSTERDAMAGE_4d10: nDamage = IP_CONST_MONSTERDAMAGE_4d12; break; + case IP_CONST_MONSTERDAMAGE_4d12: nDamage = IP_CONST_MONSTERDAMAGE_5d8; break; + + // 5-dice increments + case IP_CONST_MONSTERDAMAGE_5d4: nDamage = IP_CONST_MONSTERDAMAGE_5d6; break; + case IP_CONST_MONSTERDAMAGE_5d6: nDamage = IP_CONST_MONSTERDAMAGE_5d8; break; + case IP_CONST_MONSTERDAMAGE_5d8: nDamage = IP_CONST_MONSTERDAMAGE_5d10; break; + case IP_CONST_MONSTERDAMAGE_5d10: nDamage = IP_CONST_MONSTERDAMAGE_5d12; break; + case IP_CONST_MONSTERDAMAGE_5d12: nDamage = IP_CONST_MONSTERDAMAGE_6d10; break; + + // 6-dice increments + //case IP_CONST_MONSTERDAMAGE_6d4: nDamage = IP_CONST_MONSTERDAMAGE_6d6; break; + case IP_CONST_MONSTERDAMAGE_6d6: nDamage = IP_CONST_MONSTERDAMAGE_6d8; break; + case IP_CONST_MONSTERDAMAGE_6d8: nDamage = IP_CONST_MONSTERDAMAGE_6d10; break; + case IP_CONST_MONSTERDAMAGE_6d10: nDamage = IP_CONST_MONSTERDAMAGE_6d12; break; + case IP_CONST_MONSTERDAMAGE_6d12: nDamage = IP_CONST_MONSTERDAMAGE_7d10; break; + + // 7-dice increments + case IP_CONST_MONSTERDAMAGE_7d4: nDamage = IP_CONST_MONSTERDAMAGE_7d6; break; + case IP_CONST_MONSTERDAMAGE_7d6: nDamage = IP_CONST_MONSTERDAMAGE_7d8; break; + case IP_CONST_MONSTERDAMAGE_7d8: nDamage = IP_CONST_MONSTERDAMAGE_7d10; break; + case IP_CONST_MONSTERDAMAGE_7d10: nDamage = IP_CONST_MONSTERDAMAGE_7d12; break; + case IP_CONST_MONSTERDAMAGE_7d12: nDamage = IP_CONST_MONSTERDAMAGE_9d10; break; + + // 8-dice increments + //case IP_CONST_MONSTERDAMAGE_8d4: nDamage = IP_CONST_MONSTERDAMAGE_8d6; break; + case IP_CONST_MONSTERDAMAGE_8d6: nDamage = IP_CONST_MONSTERDAMAGE_8d8; break; + case IP_CONST_MONSTERDAMAGE_8d8: nDamage = IP_CONST_MONSTERDAMAGE_8d10; break; + case IP_CONST_MONSTERDAMAGE_8d10: nDamage = IP_CONST_MONSTERDAMAGE_8d12; break; + case IP_CONST_MONSTERDAMAGE_8d12: nDamage = IP_CONST_MONSTERDAMAGE_10d10; break; + + // 9-dice increments + //case IP_CONST_MONSTERDAMAGE_9d4: nDamage = IP_CONST_MONSTERDAMAGE_9d6; break; + case IP_CONST_MONSTERDAMAGE_9d6: nDamage = IP_CONST_MONSTERDAMAGE_9d8; break; + case IP_CONST_MONSTERDAMAGE_9d8: nDamage = IP_CONST_MONSTERDAMAGE_9d10; break; + case IP_CONST_MONSTERDAMAGE_9d10: nDamage = IP_CONST_MONSTERDAMAGE_9d12; break; + case IP_CONST_MONSTERDAMAGE_9d12: nDamage = IP_CONST_MONSTERDAMAGE_6d20; break; + + // 10-dice increments + //case IP_CONST_MONSTERDAMAGE_10d4: nDamage = IP_CONST_MONSTERDAMAGE_10d6; break; + case IP_CONST_MONSTERDAMAGE_10d6: nDamage = IP_CONST_MONSTERDAMAGE_10d8; break; + case IP_CONST_MONSTERDAMAGE_10d8: nDamage = IP_CONST_MONSTERDAMAGE_10d10; break; + case IP_CONST_MONSTERDAMAGE_10d10: nDamage = IP_CONST_MONSTERDAMAGE_10d12; break; + case IP_CONST_MONSTERDAMAGE_10d12: nDamage = IP_CONST_MONSTERDAMAGE_7d20; break; + + // d20 increments + case IP_CONST_MONSTERDAMAGE_1d20: nDamage = IP_CONST_MONSTERDAMAGE_3d8; break; + case IP_CONST_MONSTERDAMAGE_2d20: nDamage = IP_CONST_MONSTERDAMAGE_4d12; break; + case IP_CONST_MONSTERDAMAGE_3d20: nDamage = IP_CONST_MONSTERDAMAGE_8d8; break; + case IP_CONST_MONSTERDAMAGE_4d20: nDamage = IP_CONST_MONSTERDAMAGE_8d12; break; + case IP_CONST_MONSTERDAMAGE_5d20: nDamage = IP_CONST_MONSTERDAMAGE_9d12; break; //:: Everything breaks down here + case IP_CONST_MONSTERDAMAGE_6d20: nDamage = IP_CONST_MONSTERDAMAGE_1d20; break; + case IP_CONST_MONSTERDAMAGE_7d20: nDamage = IP_CONST_MONSTERDAMAGE_8d20; break; + case IP_CONST_MONSTERDAMAGE_8d20: nDamage = IP_CONST_MONSTERDAMAGE_9d20; break; + case IP_CONST_MONSTERDAMAGE_9d20: nDamage = IP_CONST_MONSTERDAMAGE_10d20; break; + + default: break; // top tier or unknown + } + } + + return nDamage; +} + + // Clean up any extras in the inventory. void CleanExtraFists(object oCreature) { @@ -168,12 +269,169 @@ void ApplyUnarmedAttackEffects(object oCreature) } // Determines the amount of damage a character can do. -// IoDM: +1 dice at level 4, +2 dice at level 8 +// IoDM: +1 die at level 4, +2 dice at level 8 // Sacred Fist: Levels add to monk levels, or stand alone as monk levels. // Shou: 1d6 at level 1, 1d8 at level 2, 1d10 at level 3, 2d6 at level 5 // Monk: 1d6 at level 1, 1d8 at level 4, 1d10 at level 8, 2d6 at level 12, 2d8 at level 16, 2d10 at level 20 // Frostrager: 1d6 at level 1, 1d8 at level 4 int FindUnarmedDamage(object oCreature) +{ + int iDamage = 0; + int iMonk = GetLevelByClass(CLASS_TYPE_MONK, oCreature) + GetLocalInt(oCreature, "LiPengMonk"); + int iShou = GetLevelByClass(CLASS_TYPE_SHOU, oCreature); + int iBrawler = GetLevelByClass(CLASS_TYPE_BRAWLER, oCreature); + int iSacredFist = GetLevelByClass(CLASS_TYPE_SACREDFIST, oCreature); + int iEnlightenedFist = GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oCreature); + int iHenshin = GetLevelByClass(CLASS_TYPE_HENSHIN_MYSTIC, oCreature); + int iZuoken = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oCreature); + int iShadowSunNinja = GetLevelByClass(CLASS_TYPE_SHADOW_SUN_NINJA, oCreature); + int iFrost = GetLevelByClass(CLASS_TYPE_FROSTRAGER, oCreature); + int iAscetic = GetLevelByClass(CLASS_TYPE_NINJA, oCreature); + int iRonove = 0; + int iMonkDamage = 1; + int iShouDamage = 1; + int iBrawlerDamage = 1; + int iFrostDamage = 1; + int iSUSDamage = 1; + int iDieIncrease = 0; + int iSize; + + if (GetHasSpellEffect(VESTIGE_RONOVE, oCreature) && GetLevelByClass(CLASS_TYPE_BINDER, oCreature)) + iRonove = GetLocalInt(oCreature, "RonovesFists"); + + //:: Determine creature size + if( GetIsPolyMorphedOrShifted(oCreature) || GetPRCSwitch(PRC_APPEARANCE_SIZE)) + { + iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; + } + else + { + iSize = 5; // medium + if (GetHasFeat(FEAT_TINY, oCreature)) iSize = 3; + if (GetHasFeat(FEAT_SMALL, oCreature)) iSize = 4; + if (GetHasFeat(FEAT_LARGE, oCreature)) iSize = 6; + if (GetHasFeat(FEAT_HUGE, oCreature)) iSize = 7; + iSize += PRCGetCreatureSize(oCreature) - PRCGetCreatureSize(oCreature, PRC_SIZEMASK_NONE); + if (iSize < 1) iSize = 1; + if (iSize > 9) iSize = 9; + } + + // Sacred Fist code break protection + if (GetHasFeat(FEAT_SF_CODE, oCreature)) iSacredFist = 0; + + // Combine monk-like levels + iMonk += iSacredFist + iHenshin + iEnlightenedFist + iShou + iZuoken + iShadowSunNinja; + + // Superior Unarmed Strike + if (GetHasFeat(FEAT_SUPERIOR_UNARMED_STRIKE, oCreature)) + { + iMonk += 4; + int nHD = GetHitDice(oCreature); + if (nHD >= 16) iSUSDamage = IP_CONST_MONSTERDAMAGE_2d6; + else if (nHD >= 12) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d10; + else if (nHD >= 8) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d8; + else if (nHD >= 4) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d6; + else if (nHD >= 3) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d4; + } + + // Ascetic Stalker + if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) + iMonk += iAscetic; + + // Cap monk progression + if (iMonk > 16 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) iMonk = 16; + else if (iMonk > 20) iMonk = 20; + + // Ronove replacement + if (iRonove > iMonk) iMonk = iRonove; + + // Monk damage calculation (2DA row) + if (iMonk > 0) iMonkDamage = iMonk / 4 + 3; + if (iSize == 5 && iMonkDamage == 7 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) + iMonkDamage = 8; + + // Shou Disciple base damage + if (iShou > 0) + { + int nRow; + if (iShou == 1) nRow = 3; + else if (iShou == 2) nRow = 4; + else if (iShou == 3) nRow = 5; + else if (iShou == 4) nRow = 5; + else if (iShou == 5) nRow = 6; + else nRow = 3; + + if (nRow > 6) nRow = 6; + + iShouDamage = StringToInt(Get2DACache("unarmed_dmg", "size" + IntToString(iSize), nRow)); + } + + // Frostrager + if (iFrost > 0) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d6; + if (iFrost > 3) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d8; + + // Brawler + if (iBrawler > 0) iBrawlerDamage = iBrawler / 6 + 3; + if (iBrawler >= 36) iBrawlerDamage += 2; + + // Armor/shield penalties + if (iMonkDamage > 1) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + int bShieldEq = GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; + if (GetBaseAC(oArmor) > 0 || bShieldEq) + iMonkDamage = 1; + } + + if (iShouDamage > 1) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); + object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + int bShieldEq = GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD || + GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD || + GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; + if (GetBaseAC(oArmor) > 3 || bShieldEq) + iShouDamage = 1; + } + + // Determine IoDM die increase + if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease = 2; + else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease = 1; + + // Lookup monk damage in 2DA + iMonkDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iMonkDamage)); + + // 3.0e monk special cases + if (iSize <= 5 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) + { + if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d6) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d12; + if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d10) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d20; + } + + // Apply IoDM die increase last, after 2DA lookups + if (iMonkDamage > 0) iMonkDamage = StepDie(iMonkDamage, iDieIncrease); + if (iShouDamage > 0) iShouDamage = StepDie(iShouDamage, iDieIncrease); + if (iBrawlerDamage > 0) iBrawlerDamage = StepDie(iBrawlerDamage, iDieIncrease); + if (iFrostDamage > 0) iFrostDamage = StepDie(iFrostDamage, iDieIncrease); + if (iSUSDamage > 0) iSUSDamage = StepDie(iSUSDamage, iDieIncrease); + + // Select best damage + iDamage = iMonkDamage; + iDamage = (DamageAvg(iShouDamage ) > DamageAvg(iDamage)) ? iShouDamage : iDamage; + iDamage = (DamageAvg(iFrostDamage ) > DamageAvg(iDamage)) ? iFrostDamage : iDamage; + iDamage = (DamageAvg(iSUSDamage ) > DamageAvg(iDamage)) ? iSUSDamage : iDamage; + iDamage = (DamageAvg(iBrawlerDamage) > DamageAvg(iDamage)) ? iBrawlerDamage : iDamage; + + if (DEBUG) DoDebug("prc_inc_unarmed: iDamage "+IntToString(iDamage)); + + return iDamage; +} + + +/* int FindUnarmedDamage(object oCreature) { int iDamage = 0; int iMonk = GetLevelByClass(CLASS_TYPE_MONK, oCreature) + GetLocalInt(oCreature, "LiPengMonk"); @@ -195,36 +453,30 @@ int FindUnarmedDamage(object oCreature) int iDieIncrease = 0; int iSize; - if (GetHasSpellEffect(VESTIGE_RONOVE, oCreature) && GetLevelByClass(CLASS_TYPE_BINDER, oCreature)) iRonove = GetLocalInt(oCreature, "RonovesFists"); + if (GetHasSpellEffect(VESTIGE_RONOVE, oCreature) && GetLevelByClass(CLASS_TYPE_BINDER, oCreature)) + iRonove = GetLocalInt(oCreature, "RonovesFists"); - // if the creature is shifted, use model size - // otherwise, we want to stick to what the feats say they "should" be. - // No making pixies with Dragon Appearance for "huge" fist damage. - if( GetIsPolyMorphedOrShifted(oCreature) - || GetPRCSwitch(PRC_APPEARANCE_SIZE)) + // Determine creature size + if( GetIsPolyMorphedOrShifted(oCreature) || GetPRCSwitch(PRC_APPEARANCE_SIZE)) { - iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; // medium is size 5 for us + iSize = PRCGetCreatureSize(oCreature) - CREATURE_SIZE_MEDIUM + 5; } else { - // Determine creature size by feats. - iSize = 5; // medium is size 5 for us + iSize = 5; // medium if (GetHasFeat(FEAT_TINY, oCreature)) iSize = 3; if (GetHasFeat(FEAT_SMALL, oCreature)) iSize = 4; if (GetHasFeat(FEAT_LARGE, oCreature)) iSize = 6; if (GetHasFeat(FEAT_HUGE, oCreature)) iSize = 7; - // include size changes iSize += PRCGetCreatureSize(oCreature) - PRCGetCreatureSize(oCreature, PRC_SIZEMASK_NONE); - // cap if needed if (iSize < 1) iSize = 1; if (iSize > 9) iSize = 9; } - // Sacred Fist cannot add their levels if they've broken their code. + // Sacred Fist code break protection if (GetHasFeat(FEAT_SF_CODE, oCreature)) iSacredFist = 0; - // several classes add their levels to the monk class, - // or use monk progression if the character has no monk levels + // Combine monk-like levels iMonk += iSacredFist + iHenshin + iEnlightenedFist + iShou + iZuoken + iShadowSunNinja; // Superior Unarmed Strike @@ -232,49 +484,66 @@ int FindUnarmedDamage(object oCreature) { iMonk += 4; int nHD = GetHitDice(oCreature); - if (nHD >= 16) iSUSDamage = IP_CONST_MONSTERDAMAGE_2d6; + if (nHD >= 16) iSUSDamage = IP_CONST_MONSTERDAMAGE_2d6; else if (nHD >= 12) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d10; - else if (nHD >= 8) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d8; - else if (nHD >= 4) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d6; - else if (nHD >= 3) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d4; + else if (nHD >= 8) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d8; + else if (nHD >= 4) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d6; + else if (nHD >= 3) iSUSDamage = IP_CONST_MONSTERDAMAGE_1d4; } // Ascetic Stalker if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) iMonk += iAscetic; - // In 3.0e, Monk progression stops after level 16: - if (iMonk > 16 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE) ) iMonk = 16; - // in 3.5e, monk progression stops at 20. - else if(iMonk > 20) iMonk = 20; + // Cap monk progression + if (iMonk > 16 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) iMonk = 16; + else if (iMonk > 20) iMonk = 20; - // Ronove is in place of monk, does not stack + // Ronove replacement if (iRonove > iMonk) iMonk = iRonove; - // monks damage progesses every four levels, starts at 1d6 - if (iMonk > 0) - iMonkDamage = iMonk / 4 + 3; + // Monk damage calculation + if (iMonk > 0) iMonkDamage = iMonk / 4 + 3; - // For medium monks in 3.0e skip 2d8 and go to 1d20 - if(iSize == 5 && iMonkDamage == 7 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE) ) iMonkDamage = 8; + if(iSize == 5 && iMonkDamage == 7 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) + iMonkDamage = 8; // Shou Disciple either adds its level to existing class or does its own damage, depending // on which is better. Here we will determine how much damage the Shou Disciple does // without stacking. - if (iShou > 0) iShouDamage = iShou + 2; // Lv. 1: 1d6, Lv. 2: 1d8, Lv. 3: 1d10 - if (iShou > 3) iShouDamage--; // Lv. 4: 1d10, Lv. 5: 2d6 - iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); - - // Frostrager does not stack with other damage types - if (iFrost > 0) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d6; // Lv. 1: 1d6 - if (iFrost > 3) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d8; // Lv. 3: 1d8 + //if (iShou > 0) iShouDamage = iShou + 2; // Lv. 1: 1d6, Lv. 2: 1d8, Lv. 3: 1d10 + //if (iShou > 3) iShouDamage--; // Lv. 4: 1d10, Lv. 5: 2d6 + //iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); + + if (iShou > 0) + { + // Determine 2DA row for Shou progression + int nRow; + if (iShou == 1) nRow = 3; // monk1 + else if (iShou == 2) nRow = 4; // monk2 + else if (iShou == 3) nRow = 5; // monk3 + else if (iShou == 4) nRow = 6; // monk4 + else if (iShou == 5) nRow = 7; // monk5 + else if (iShou == 6) nRow = 8; // monk6 + else if (iShou == 7) nRow = 9; // monk7 + else nRow = 10; // monk8+ + + nRow += iDieIncrease; + if (nRow > 10) nRow = 10; // clamp to max row - // Brawler follows monk progression except for the last one (3d8) - if (iBrawler > 0) iBrawlerDamage = iBrawler / 6 + 3; // 1d6, 1d8, 1d10, 2d6, 2d8, 2d10 - if (iBrawler >= 36) iBrawlerDamage += 2; // 3d8 + // Lookup damage in unarmed_damage.2da using size column + iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), nRow)); + } - // Monks and monk-like classes deal no additional damage when wearing any armor, at - // least in NWN. This is to reflect that. No shields too. + // Frostrager + if (iFrost > 0) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d6; + if (iFrost > 3) iFrostDamage = IP_CONST_MONSTERDAMAGE_1d8; + + // Brawler + if (iBrawler > 0) iBrawlerDamage = iBrawler / 6 + 3; + if (iBrawler >= 36) iBrawlerDamage += 2; + + // Armor/shield penalties if (iMonkDamage > 1) { object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); @@ -284,13 +553,10 @@ int FindUnarmedDamage(object oCreature) GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; if (GetBaseAC(oArmor) > 0 || bShieldEq) - { iMonkDamage = 1; - } } -// Shou Disciples can wear light armor - if (iShouDamage > 1) + if (iShouDamage > 1) { object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature); object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); @@ -299,53 +565,31 @@ int FindUnarmedDamage(object oCreature) GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD; if (GetBaseAC(oArmor) > 3 || bShieldEq) - { - iShouDamage = 1; - } + iShouDamage = 1; } - // For Initiate of Draconic Mysteries - if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease = 2; - else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease = 1; - - //:: Expansion / Compression powers - int nExpansion = GetLocalInt(oCreature, "PRC_Power_Expansion_SizeIncrease"); - int nCompression = GetLocalInt(oCreature, "PRC_Power_Compression_SizeReduction"); - - if (nExpansion) - { - iSize += nExpansion; - } - - if (nCompression) - { - iSize -= nCompression; - } + // IoDM die increase + if (GetHasFeat(FEAT_INCREASE_DAMAGE2, oCreature)) iDieIncrease += 2; + else if (GetHasFeat(FEAT_INCREASE_DAMAGE1, oCreature)) iDieIncrease += 1; iMonkDamage += iDieIncrease; iShouDamage += iDieIncrease; iBrawlerDamage += iDieIncrease; iFrostDamage += iDieIncrease; iSUSDamage += iDieIncrease; - - //FloatingTextStringOnCreature("prc_inc_unarmed: Size is: "+IntToString(iSize)+".", oCreature); - //FloatingTextStringOnCreature("prc_inc_unarmed: Pre 2DA Lookup >> iMonkDamage = "+IntToString(iMonkDamage)+".", oCreature); - // now, read the damage from the table in unarmed_dmg.2da + // Lookup final monk damage in 2DA iMonkDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iMonkDamage)); - iShouDamage = StringToInt(Get2DACache("unarmed_dmg","size" + IntToString(iSize), iShouDamage)); - - //FloatingTextStringOnCreature("prc_inc_unarmed: Post 2DA Lookup >> iMonkDamage = "+IntToString(iMonkDamage)+".", oCreature); - // Medium+ monks have some special values on the table in 3.0: + // 3.0e monk special cases if (iSize >= 5 && !GetPRCSwitch(PRC_3_5e_FIST_DAMAGE)) { if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d6) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d12; if (iMonkDamage == IP_CONST_MONSTERDAMAGE_2d10) iMonkDamage = IP_CONST_MONSTERDAMAGE_1d20; } + // Select best damage iDamage = iMonkDamage; - // Future unarmed classes: if you do your own damage, add in "comparisons" below here. iDamage = (DamageAvg(iShouDamage ) > DamageAvg(iDamage)) ? iShouDamage : iDamage; iDamage = (DamageAvg(iFrostDamage ) > DamageAvg(iDamage)) ? iFrostDamage : iDamage; iDamage = (DamageAvg(iSUSDamage ) > DamageAvg(iDamage)) ? iSUSDamage : iDamage; @@ -354,6 +598,8 @@ int FindUnarmedDamage(object oCreature) return iDamage; } + */ + // Adds appropriate feats to the skin. Stolen from SoulTaker + expanded with overwhelming/devastating critical. void UnarmedFeats(object oCreature) @@ -426,13 +672,13 @@ void UnarmedFists(object oCreature) // Sacred Fists who break their code get no benefits. if (GetHasFeat(FEAT_SF_CODE,oCreature)) iSacFist = 0; - + // The monk adds all these classes. - int iMonkEq = iMonk + iShou + iSacFist + iHenshin + iZuoken + iShadowSunNinja; - + int iMonkEq = iMonk + iShou + iSacFist + iHenshin + iZuoken + iShadowSunNinja; + // Ascetic Stalker if (GetHasFeat(FEAT_ASCETIC_STALKER, oCreature)) - iMonkEq += iAscetic; + iMonkEq += iAscetic; // Determine the type of damage the character should do. string sWeapType; diff --git a/src/include/prc_inc_wpnrest.nss b/src/include/prc_inc_wpnrest.nss index 9dcf65a..0a59892 100644 --- a/src/include/prc_inc_wpnrest.nss +++ b/src/include/prc_inc_wpnrest.nss @@ -1,6 +1,6 @@ //:://///////////////////////////////////////////// //:: Weapon Restriction System Include -//:: prc_inc_restwpn.nss +//:: prc_inc_wpnrest.nss //:://///////////////////////////////////////////// /* Functions to support PnP Weapon Proficiency and @@ -15,6 +15,56 @@ #include "inc_item_props" #include "prc_x2_itemprop" +//:: Detects if "monk" gloves are being equipped & set a +//:: variable if TRUE for use with other functions +void DetectMonkGloveEquip(object oItem) +{ + int nItemType = GetBaseItemType(oItem); + + object oPC = GetItemPossessor(oItem); + if (!GetIsObjectValid(oItem)) + { + if (DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloveEquip(): Unable to determine item possessor"); + return; + } + + if(nItemType != BASE_ITEM_GLOVES && nItemType != BASE_ITEM_BRACER) {return;} + + else if (nItemType == BASE_ITEM_BRACER) + { + if(DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloveEquip(): Bracer found!"); + DeleteLocalInt(oPC, "WEARING_MONK_GLOVES"); + return; + } + else + { + itemproperty ipG = GetFirstItemProperty(oItem); + + while(GetIsItemPropertyValid(ipG)) + { + int nTypeG = GetItemPropertyType(ipG); + + // Damage related properties we care about + if(nTypeG == ITEM_PROPERTY_DAMAGE_BONUS + || nTypeG == ITEM_PROPERTY_ATTACK_BONUS + || nTypeG == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP + || nTypeG == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nTypeG == ITEM_PROPERTY_DAMAGE_BONUS_VS_SPECIFIC_ALIGNMENT) + { + if(DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloves(): Monk gloves found!"); + SetLocalInt(oPC, "WEARING_MONK_GLOVES", 1); + return; + } + else + { + if(DEBUG) DoDebug("prc_inc_wpnrest >> DetectMonkGloves(): Monk gloves not found! You should never see this."); + DeleteLocalInt(oPC, "WEARING_MONK_GLOVES"); + return; + } + } + } +} + /** * All of the following functions use the following parameters: * @@ -23,6 +73,69 @@ * @param nHand The hand the weapon is wielded in. In the form of * ATTACK_BONUS_ONHAND or ATTACK_BONUS_OFFHAND. */ +//:: returns TRUE if the wielded weapon works with the Swashbuckler's class abilities. +int GetHasSwashbucklerWeapon(object oPC) +{ + object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (!GetIsObjectValid(oWeap)) return FALSE; + + int nType = GetBaseItemType(oWeap); + + switch (nType) + { + case BASE_ITEM_DAGGER: + case BASE_ITEM_KATAR: + case BASE_ITEM_HANDAXE: + case BASE_ITEM_KAMA: + case BASE_ITEM_KUKRI: + case BASE_ITEM_LIGHTHAMMER: + case BASE_ITEM_LIGHTMACE: + case BASE_ITEM_LIGHT_PICK: + case BASE_ITEM_RAPIER: + case BASE_ITEM_SHORTSWORD: + case BASE_ITEM_SICKLE: + case BASE_ITEM_WHIP: + case BASE_ITEM_SAI: + case BASE_ITEM_SAP: + case BASE_ITEM_NUNCHAKU: + case BASE_ITEM_GOAD: + case BASE_ITEM_ELVEN_LIGHTBLADE: + case BASE_ITEM_ELVEN_THINBLADE: + case BASE_ITEM_EAGLE_CLAW: + return TRUE; + } + + // Iaijutsu Master allows katana + if (GetLevelByClass(CLASS_TYPE_IAIJUTSU_MASTER, oPC) > 0) + { + if (nType == BASE_ITEM_KATANA) return TRUE; + } + + return FALSE; +} + +//:: returns TRUE if the wielded weapon works with the Champion of Corellon's Elegant Strike. +int GetHasCorellonWeapon(object oPC) +{ + object oWeap = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + if (!GetIsObjectValid(oWeap)) return FALSE; + + int nType = GetBaseItemType(oWeap); + + switch (nType) + { + case BASE_ITEM_SCIMITAR: + case BASE_ITEM_LONGSWORD: + case BASE_ITEM_RAPIER: + case BASE_ITEM_ELVEN_COURTBLADE: + case BASE_ITEM_ELVEN_LIGHTBLADE: + case BASE_ITEM_ELVEN_THINBLADE: + return TRUE; + } + + return FALSE; +} + void DoRacialEquip(object oPC, int nBaseType); //return if PC has proficiency in an item @@ -763,6 +876,7 @@ int IsMeleeWeapon(int nBaseItemType) case BASE_ITEM_CLOAK: case BASE_ITEM_CRAFTED_ROD: case BASE_ITEM_CRAFTED_STAFF: + case BASE_ITEM_CRAFTED_SCEPTER: case BASE_ITEM_CRAFTMATERIALMED: case BASE_ITEM_CRAFTMATERIALSML: case BASE_ITEM_CREATUREITEM: diff --git a/src/include/prc_ipfeat_const.nss b/src/include/prc_ipfeat_const.nss index d419f0b..672fd04 100644 --- a/src/include/prc_ipfeat_const.nss +++ b/src/include/prc_ipfeat_const.nss @@ -262,7 +262,7 @@ const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_LANCE = 4638; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_HEAVY_PICK = 4639; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_LIGHT_PICK = 4640; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SAI = 4641; -const int IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHUKU = 4642; +const int IP_CONST_FEAT_WEAPON_PROFICIENCY_NUNCHAKU = 4642; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_FALCHION = 4643; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_SAP = 4644; const int IP_CONST_FEAT_WEAPON_PROFICIENCY_KATAR = 4645; diff --git a/src/include/prc_misc_const.nss b/src/include/prc_misc_const.nss index b083a8e..0171bbb 100644 --- a/src/include/prc_misc_const.nss +++ b/src/include/prc_misc_const.nss @@ -29,8 +29,8 @@ const int BASE_ITEM_CRAFTED_STAFF = 201; const int BASE_ITEM_ELVEN_LIGHTBLADE = 202; const int BASE_ITEM_ELVEN_THINBLADE = 203; const int BASE_ITEM_ELVEN_COURTBLADE = 204; -const int BASE_ITEM_CRAFT_SCEPTER = 249; -const int BASE_ITEM_MAGIC_SCEPTER = 250; +const int BASE_ITEM_CRAFTED_SCEPTER = 249; +const int BASE_ITEM_CRAFTED_VIAL = 250; const int BASE_ITEM_MUNDANE_HERB = 252; const int BASE_ITEM_INFUSED_HERB = 253; diff --git a/src/include/prc_nui_com_inc.nss b/src/include/prc_nui_com_inc.nss index bf9bc51..4bba080 100644 --- a/src/include/prc_nui_com_inc.nss +++ b/src/include/prc_nui_com_inc.nss @@ -3,6 +3,7 @@ #include "psi_inc_psifunc" #include "inc_lookups" #include "nw_inc_nui" +#include "tob_inc_tobfunc" // // GetCurrentSpellLevel @@ -153,8 +154,53 @@ json GreyOutButton(json jButton, float w, float h); // json CreateGreyOutRectangle(float w, float h); +// +// GetTrueClassType +// Gets the true class Id for a provided class Id, mostly for RHD and for +// ToB prestige classes +// +// Arguments: +// nClass:int classId +// +// Returns: +// int the true classId based off nClass +// +int GetTrueClassType(int nClass, object oPC=OBJECT_SELF); + void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0, int nClass=0); + +void CallSpellUnlevelScript(object oPC, int nClass, int nLevel); void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF); +void RemoveIPFeat(object oPC, int ipFeatID); + +void CallSpellUnlevelScript(object oPC, int nClass, int nLevel) +{ + SetScriptParam("UnLevel_ClassChoice", IntToString(nClass)); + SetScriptParam("UnLevel_LevelChoice", IntToString(nLevel)); + ExecuteScript("prc_unlvl_script", oPC); +} + +void RemoveIPFeat(object oPC, int ipFeatID) +{ + object oSkin = GetPCSkin(oPC); + itemproperty ipTest = GetFirstItemProperty(oSkin); + while(GetIsItemPropertyValid(ipTest)) + { + // Check if the itemproperty is a bonus feat that has been marked for removal + if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT) + { + if (GetItemPropertySubType(ipTest) == ipFeatID) + { + if(DEBUG) DoDebug("_ManeuverRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest)); + // If so, remove it + RemoveItemProperty(oSkin, ipTest); + } + + } + + ipTest = GetNextItemProperty(oSkin); + } +} int GetCurrentSpellLevel(int nClass, int nLevel) { @@ -401,6 +447,9 @@ json GetSpellIcon(int spellId,int featId=0,int nClass=0) // the FeatID holds the accurate spell icon, not the SpellID int nFeatID = StringToInt(Get2DACache("spells", "FeatID", spellId)); + // however if no featId was found use the spell's icon instead + if (!nFeatID) + return JsonString(Get2DACache("spells", "IconResRef", spellId)); return JsonString(Get2DACache("feat", "Icon", nFeatID)); } @@ -528,3 +577,39 @@ void ClearSpellDescriptionNUI(object oPlayer=OBJECT_SELF) DeleteLocalInt(oPlayer, NUI_SPELL_DESCRIPTION_CLASSID_VAR); } +int GetTrueClassType(int nClass, object oPC=OBJECT_SELF) +{ + if (nClass == CLASS_TYPE_JADE_PHOENIX_MAGE + || nClass == CLASS_TYPE_MASTER_OF_NINE + || nClass == CLASS_TYPE_DEEPSTONE_SENTINEL + || nClass == CLASS_TYPE_BLOODCLAW_MASTER + || nClass == CLASS_TYPE_RUBY_VINDICATOR + || nClass == CLASS_TYPE_ETERNAL_BLADE + || nClass == CLASS_TYPE_SHADOW_SUN_NINJA) + { + int trueClass = GetPrimaryBladeMagicClass(oPC); + return trueClass; + } + + if ((nClass == CLASS_TYPE_SHAPECHANGER + && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) + || (nClass == CLASS_TYPE_OUTSIDER + && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) + || (nClass == CLASS_TYPE_ABERRATION + && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) + || (nClass == CLASS_TYPE_MONSTROUS + && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT)) + return CLASS_TYPE_SORCERER; + if (nClass == CLASS_TYPE_FEY + && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) + return CLASS_TYPE_BARD; + + return nClass; +} + diff --git a/src/include/prc_nui_consts.nss b/src/include/prc_nui_consts.nss index e1e9c5c..745c860 100644 --- a/src/include/prc_nui_consts.nss +++ b/src/include/prc_nui_consts.nss @@ -156,3 +156,16 @@ const string NUI_SPELL_DESCRIPTION_CLASSID_VAR = "NUISpellDescriptionClassID"; const string NUI_SPELL_DESCRIPTION_SPELLID_VAR = "NUISpellDescriptionSpellID"; const string NUI_SPELL_DESCRIPTION_REAL_SPELLID_VAR = "NUISpellDescriptionRealSpellID"; + +////////////////////////////////////////////////// +// // +// Spell Duration NUI // +// // +////////////////////////////////////////////////// + +const string DURATION_NUI_WINDOW_ID = "DurationNUI"; +const string NUI_DURATION_MANUALLY_OPENED_PARAM = "DurationNUIManuallyOpenedParam"; +const string NUI_DURATION_NO_LOOP_PARAM = "DurationNUINoLoopParam"; +const string NUI_DURATION_TRACKED_SPELLS = "durationNUI_trackedSpellList"; +const string NUI_SPELL_DURATION_BASE_BIND = "durationNUI_durationSpellId"; +const string NUI_SPELL_DURATION_SPELLID_BASE_CANCEL_BUTTON = "NuiDurationCancelButtonSpellID"; diff --git a/src/include/prc_nui_lv_inc.nss b/src/include/prc_nui_lv_inc.nss index bc10b28..b3d862f 100644 --- a/src/include/prc_nui_lv_inc.nss +++ b/src/include/prc_nui_lv_inc.nss @@ -98,21 +98,9 @@ void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF); // // CloseNUILevelUpWindow // Closes the NUI Level Up Window if its open +// setting reset to 1 will make it clear the entire cache as if the NUI was never opened // -void CloseNUILevelUpWindow(object oPC=OBJECT_SELF); - -// -// GetTrueClassType -// Gets the true class Id for a provided class Id, mostly for RHD and for -// ToB prestige classes -// -// Arguments: -// nClass:int classId -// -// Returns: -// int the true classId based off nClass -// -int GetTrueClassType(int nClass, object oPC=OBJECT_SELF); +void CloseNUILevelUpWindow(object oPC=OBJECT_SELF, int reset=0); // // GetRemainingSpellChoices @@ -432,7 +420,7 @@ json GetChosenReplaceListObject(object oPC=OBJECT_SELF); // Returns: // int:Boolean TRUE if the spell is a expanded knowledge spell, FALSE otherwise // -int IsExpKnowledgePower(int nClass, int spellbookId); +int IsExpKnowledgePower(int nClass, int spellbookId, object oPC=OBJECT_SELF); // // GetExpKnowledgePowerListRequired @@ -861,7 +849,7 @@ void AddSpellToChosenList(int nClass, int spellbookId, int spellCircle, object o // if the power is a expanded knowledge than we immediatly add it to the // extra list, otherwise check to make sure we have made all choices in our // base list first before adding it to the extra list. - if (IsExpKnowledgePower(nClass, spellbookId) + if (IsExpKnowledgePower(nClass, spellbookId, oPC) || GetRemainingPowerChoices(nClass, spellCircle, oPC, FALSE) == 0) { string sFile = GetClassSpellbookFile(nClass); @@ -1061,53 +1049,20 @@ void OpenNUILevelUpWindow(int nClass, object oPC=OBJECT_SELF) // figure out what the true base class is (mostly true for RHD) int chosenClass = GetTrueClassType(nClass, oPC); SetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR, chosenClass); - ExecuteScript("prc_nui_lv_view", oPC); } -int GetTrueClassType(int nClass, object oPC=OBJECT_SELF) -{ - if (nClass == CLASS_TYPE_JADE_PHOENIX_MAGE - || nClass == CLASS_TYPE_MASTER_OF_NINE - || nClass == CLASS_TYPE_DEEPSTONE_SENTINEL - || nClass == CLASS_TYPE_BLOODCLAW_MASTER - || nClass == CLASS_TYPE_RUBY_VINDICATOR - || nClass == CLASS_TYPE_ETERNAL_BLADE - || nClass == CLASS_TYPE_SHADOW_SUN_NINJA) - { - int trueClass = GetPrimaryBladeMagicClass(oPC); - return trueClass; - } - - if ((nClass == CLASS_TYPE_SHAPECHANGER - && GetRacialType(oPC) == RACIAL_TYPE_ARANEA) - || (nClass == CLASS_TYPE_OUTSIDER - && GetRacialType(oPC) == RACIAL_TYPE_RAKSHASA) - || (nClass == CLASS_TYPE_ABERRATION - && GetRacialType(oPC) == RACIAL_TYPE_DRIDER) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_ARKAMOI) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_HOBGOBLIN_WARSOUL) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_REDSPAWN_ARCANISS) - || (nClass == CLASS_TYPE_MONSTROUS - && GetRacialType(oPC) == RACIAL_TYPE_MARRUTACT)) - return CLASS_TYPE_SORCERER; - if (nClass == CLASS_TYPE_FEY - && GetRacialType(oPC) == RACIAL_TYPE_GLOURA) - return CLASS_TYPE_BARD; - - return nClass; -} - -void CloseNUILevelUpWindow(object oPC=OBJECT_SELF) +void CloseNUILevelUpWindow(object oPC=OBJECT_SELF, int reset=0) { int currentClass = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CLASS_VAR); // if we are refreshing the NUI but not finished we need to clear some caching done // to save computation time as they will need to be reprocessed. DeleteLocalJson(oPC, NUI_LEVEL_UP_DISCIPLINE_INFO_VAR + IntToString(currentClass)); SetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR, -20); + if (reset) + { + ClearLevelUpNUICaches(currentClass, oPC); + } int nPreviousToken = NuiFindWindow(oPC, NUI_LEVEL_UP_WINDOW_ID); if (nPreviousToken != 0) { @@ -1126,7 +1081,7 @@ int ShouldSpellButtonBeEnabled(int nClass, int circleLevel, int spellbookId, obj // if its an expanded knowledge choice and we have already made all our // exp knowledge choices then it needs to be disabled. - if (IsExpKnowledgePower(nClass, spellbookId)) + if (IsExpKnowledgePower(nClass, spellbookId, oPC)) { int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC) + GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC); @@ -1540,7 +1495,7 @@ int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF totalSpellsKnown = GetSpellKnownMaxCount(casterLevel, circleLevel, nClass, oPC); // Favoured Soul has more 0 choices than there are spells for some reason if (nClass == CLASS_TYPE_FAVOURED_SOUL && circleLevel == 0 && totalSpellsKnown > 6) - totalSpellsKnown = 6; + totalSpellsKnown = 7; // logic for spont casters json selectedCircle = JsonObjectGet(chosenSpells, IntToString(circleLevel)); @@ -1554,6 +1509,7 @@ int GetRemainingSpellChoices(int nClass, int circleLevel, object oPC=OBJECT_SELF if (chosenCircle == circleLevel) SetLocalInt(oPC, NUI_LEVEL_UP_REMAINING_CHOICES_CACHE_VAR, remainingChoices); + if (DEBUG) DoDebug("Remaining spell choices is " + IntToString(remainingChoices)); return remainingChoices; } @@ -1664,11 +1620,13 @@ void RemoveSpells(int nClass, object oPC=OBJECT_SELF) { string sFile = GetClassSpellbookFile(nClass); string sSpellBook = GetSpellsKnown_Array(nClass); + string spellsAtLevelList = "SpellsKnown_" + IntToString(nClass) + "_AtLevel" + IntToString(GetHitDice(oPC)); // remove the spell from the spellbook array_extract_int(oPC, sSpellBook, nSpellbookID); + array_extract_int(oPC, spellsAtLevelList, nSpellbookID); // wipe the spell from the player int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)); - WipeSpellFromHide(ipFeatID, oPC); + RemoveIPFeat(oPC, ipFeatID); } } } @@ -1779,6 +1737,7 @@ void LearnSpells(int nClass, object oPC=OBJECT_SELF) // get location of persistant storage on the hide string sSpellbook = GetSpellsKnown_Array(nClass, nSpellLevel); + if (DEBUG) DoDebug("Adding spell " + IntToString(nSpellbookID) + "to " + sSpellbook); //object oToken = GetHideToken(oPC); // Create spells known persistant array if it is missing @@ -1789,13 +1748,25 @@ void LearnSpells(int nClass, object oPC=OBJECT_SELF) nSize = 0; } + string spellsAtLevelList = "SpellsKnown_" + IntToString(nClass) + "_AtLevel" + IntToString(GetHitDice(oPC)); + int spellsAtLevelSize = persistant_array_get_size(oPC, spellsAtLevelList); + if (spellsAtLevelSize < 0) + { + persistant_array_create(oPC, spellsAtLevelList); + spellsAtLevelSize = 0; + } + // set the list of spells learned at this level + string sFile = GetClassSpellbookFile(nClass); + int spellId = StringToInt(Get2DACache(sFile, "SpellID", nSpellbookID)); + persistant_array_set_int(oPC, spellsAtLevelList, spellsAtLevelSize, spellId); + if (DEBUG) DoDebug("Adding spells to array " + spellsAtLevelList); + // Mark the spell as known (e.g. add it to the end of oPCs spellbook) persistant_array_set_int(oPC, sSpellbook, nSize, nSpellbookID); if (nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS) { // add spell - string sFile = GetClassSpellbookFile(nClass); string sArrayName = "NewSpellbookMem_" + IntToString(nClass); int featId = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookID)); int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", nSpellbookID)); @@ -1975,7 +1946,7 @@ void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int n //if we could not find the spell here, something went wrong if (persistant_array_extract_int(oPC, sTestArray, spellID) < 0) { - SendMessageToPC(oPC, "Could not find spellID " + IntToString(spellID) + " in the class's spellbook!"); + if (DEBUG) DoDebug("Could not find spellID " + IntToString(spellID) + " in the class's spellbook!"); return; } } @@ -2002,10 +1973,7 @@ void RemoveSpellKnown(int nClass, int spellbookId, object oPC=OBJECT_SELF, int n // remove spell from player int ipFeatID = StringToInt(Get2DACache(sFile, "IPFeatID", spellbookId)); - itemproperty ipFeat = PRCItemPropertyBonusFeat(ipFeatID); - object oSkin = GetPCSkin(oPC); - RemoveItemProperty(oSkin, ipFeat); - CheckAndRemoveFeat(oSkin, ipFeat); + RemoveIPFeat(oPC, ipFeatID); } json GetSpellIDsKnown(int nClass, object oPC=OBJECT_SELF, int nList=0) @@ -2088,7 +2056,7 @@ string ReasonForDisabledSpell(int nClass, int spellbookId, object oPC=OBJECT_SEL // if its an expanded knowledge choice and we have already made all our // exp knowledge choices then it needs to be disabled. - if (IsExpKnowledgePower(nClass, spellbookId)) + if (IsExpKnowledgePower(nClass, spellbookId, oPC)) { int remainingExp = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC) + GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC); @@ -2322,11 +2290,15 @@ json GetChosenReplaceListObject(object oPC=OBJECT_SELF) //////////////////////////////////////////////////////////////////////////// -int IsExpKnowledgePower(int nClass, int spellbookId) +int IsExpKnowledgePower(int nClass, int spellbookId, object oPC=OBJECT_SELF) { string sFile = GetClassSpellbookFile(nClass); int isExp = StringToInt(Get2DACache(sFile, "Exp", spellbookId)); - return isExp; + if (isExp) + return TRUE; + int featId = StringToInt(Get2DACache(sFile, "FeatID", spellbookId)); + int isOuterDomain = (featId) ? !CheckPowerPrereqs(featId, oPC) : FALSE; + return isOuterDomain; } json GetCurrentPowerList(object oPC=OBJECT_SELF) @@ -2346,12 +2318,8 @@ int ShouldAddPower(int nClass, int spellbookId, object oPC=OBJECT_SELF) string sFile = GetClassSpellbookFile(nClass); int featId = StringToInt(Get2DACache(sFile, "FeatID", spellbookId)); int isExp = StringToInt(Get2DACache(sFile, "Exp", spellbookId)); - // if you don't have the prereqs for a power then don't add it. Specific for - // psions - if (!CheckPowerPrereqs(featId, oPC)) - return FALSE; // if the power is a expanded knowledge power - if (isExp) + if (!CheckPowerPrereqs(featId, oPC) || isExp) { // and we have a expanded knowledge choice left to make then show // the button @@ -2360,10 +2328,12 @@ int ShouldAddPower(int nClass, int spellbookId, object oPC=OBJECT_SELF) int currentCircle = GetLocalInt(oPC, NUI_LEVEL_UP_SELECTED_CIRCLE_VAR); int choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EXP_KNOWLEDGE, oPC); + if (DEBUG) DoDebug("You still have " + IntToString(choicesLeft) + " expanded power choices left!"); if (choicesLeft && (currentCircle <= (maxLevel-1))) addPower = TRUE; choicesLeft = GetRemainingExpandedChoices(nClass, POWER_LIST_EPIC_EXP_KNOWLEDGE, oPC); - if (choicesLeft) + if (DEBUG) DoDebug("You still have " + IntToString(choicesLeft) + " epic expanded power choices left!"); + if (choicesLeft && (currentCircle <= (maxLevel-1))) addPower = TRUE; // otherwise don't show the button. return addPower; @@ -2383,7 +2353,7 @@ void LearnPowers(int nClass, object oPC=OBJECT_SELF) int nSpellbookID = JsonGetInt(JsonArrayGet(powerList, i)); // get the expanded knowledge list we are adding to if any int expKnow = GetExpKnowledgePowerListRequired(nClass, nSpellbookID, oPC); - AddPowerKnown(oPC, nClass, nSpellbookID, TRUE, GetManifesterLevel(oPC, nClass, TRUE), expKnow); + AddPowerKnown(oPC, nClass, nSpellbookID, TRUE, GetHitDice(oPC), expKnow); } } @@ -2570,7 +2540,7 @@ int HasPreRequisitesForManeuver(int nClass, int spellbookId, object oPC=OBJECT_S if (chosenDisc != JsonNull()) { int nManCount = (JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_MANEUVER))) - + JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_STANCE)))); + + JsonGetInt(JsonObjectGet(chosenDisc, IntToString(MANEUVER_TYPE_STANCE)))); if (nManCount >= prereqs) return TRUE; } @@ -3166,13 +3136,18 @@ json GetInvokerKnownListObject(int nClass, object oPC=OBJECT_SELF) } SetLocalJson(oPC, NUI_LEVEL_UP_KNOWN_INVOCATIONS_CACHE_VAR + IntToString(nClass), knownObject); + if (DEBUG) DoDebug("Printing json representation of allowed invocations for class " + IntToString(nClass)); + if (DEBUG) DoDebug(JsonDump(knownObject, 2)); return knownObject; } int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJECT_SELF, int extra=TRUE) { + if (DEBUG) DoDebug ("Getting remaining invocation choices at " + IntToString(chosenCircle) + " circle"); int remaining = 0; int nLevel = GetInvokerLevel(oPC, nClass); + if (nClass == CLASS_TYPE_DRAGON_SHAMAN) nLevel = GetLevelByClass(nClass, oPC); + if (DEBUG) DoDebug("Invoker level is " + IntToString(nLevel)); json knownObject = GetInvokerKnownListObject(nClass, oPC); json chosenInv = GetChosenSpellListObject(nClass, oPC); @@ -3200,8 +3175,10 @@ int GetRemainingInvocationChoices(int nClass, int chosenCircle, object oPC=OBJEC currentChosen += 1; } } + if (DEBUG) DoDebug(IntToString(currentChosen) + " incantations chosen at " + IntToString(chosenCircle) + " circle"); int allowedAtCircle = JsonGetInt(JsonObjectGet(currentLevelKnown, IntToString(i))); + if (DEBUG) DoDebug(IntToString(allowedAtCircle) + " incantations allowed at " + IntToString(chosenCircle) + " circle"); remaining = (allowedAtCircle - currentChosen + remaining); // if the circle is below the chosen circle and we have a positive remaining, diff --git a/src/include/prc_nui_sbd_inc.nss b/src/include/prc_nui_sbd_inc.nss index 4f84658..8027a70 100644 --- a/src/include/prc_nui_sbd_inc.nss +++ b/src/include/prc_nui_sbd_inc.nss @@ -28,11 +28,17 @@ void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int re void CreateSpellDescriptionNUI(object oPlayer, int featID, int spellId=0, int realSpellId=0) { // look for existing window and destroy - int nPreviousToken = NuiFindWindow(OBJECT_SELF, NUI_SPELL_DESCRIPTION_WINDOW_ID); + int nPreviousToken = NuiFindWindow(oPlayer, NUI_SPELL_DESCRIPTION_WINDOW_ID); + if(nPreviousToken != 0) + { + NuiDestroy(oPlayer, nPreviousToken); + } + +/* int nPreviousToken = NuiFindWindow(OBJECT_SELF, NUI_SPELL_DESCRIPTION_WINDOW_ID); if(nPreviousToken != 0) { NuiDestroy(OBJECT_SELF, nPreviousToken); - } + } */ // in order of accuracy for names it goes RealSpellID > SpellID > FeatID string spellName; diff --git a/src/include/prc_sp_func.nss b/src/include/prc_sp_func.nss index 233090f..c249c5f 100644 --- a/src/include/prc_sp_func.nss +++ b/src/include/prc_sp_func.nss @@ -180,6 +180,24 @@ void RunImpactScript(object oPC, int nSpellID, int nEventType) DeleteLocalInt(oPC, PRC_SPELLID_OVERRIDE); } +//Returns true if the spell is one of the repair spells +int IsRepair(int nSpellID) +{ + return ((nSpellID >= SPELL_REPAIR_MINOR_DAMAGE) && (nSpellID <= SPELL_REPAIR_CRITICAL_DAMAGE)); +} + +//Returns true if the spell is one of the mass repair spells +int IsMassRepair(int nSpellID) +{ + return ((nSpellID >= SPELL_MASS_REPAIR_LIGHT_DAMAGE) && (nSpellID <= SPELL_MASS_REPAIR_CRITICAL_DAMAGE)); +} + +//Returns true if the spell is one of the mass inflict damage spells +int IsMassInflictDamage(int nSpellID) +{ + return ((nSpellID >= SPELL_MASS_INFLICT_LIGHT_DAMAGE) && (nSpellID <= SPELL_MASS_INFLICT_CRITICAL_DAMAGE)); +} + //Returns true if the spell is one of the cure spells int IsCure(int nSpellID) { diff --git a/src/include/prc_spell_const.nss b/src/include/prc_spell_const.nss index 02a3a2f..c93417f 100644 --- a/src/include/prc_spell_const.nss +++ b/src/include/prc_spell_const.nss @@ -13,6 +13,9 @@ const int SPELL_BLACKLIGHT = 2091; const int SPELL_BARD_SONG = 411; const int SPELL_BARD_CURSE_SONG = 644; +//:: Monk +const int SPELL_MONK_ABUNDANT_STEP = 17986; + //:: Epic Level Handbook const int SPELL_EPIC_SWARM_OF_ARROWS = 17996; @@ -1359,8 +1362,27 @@ const int SPELL_SUMMON_NATURES_ALLY_9_ARANEA = 17085; const int SPELL_CHASING_PERFECTION = 2479; //:: Spell Compendium Spells -const int SPELL_SPIRIT_WORM = 17248; -const int SPELL_FORCE_MISSILES = 2480; +const int SPELL_FORCE_MISSILES = 2480; +const int SPELL_REPAIR_MINOR_DAMAGE = 17094; +const int SPELL_REPAIR_LIGHT_DAMAGE = 17095; +const int SPELL_REPAIR_MODERATE_DAMAGE = 17096; +const int SPELL_REPAIR_SERIOUS_DAMAGE = 17097; +const int SPELL_REPAIR_CRITICAL_DAMAGE = 17098; +const int SPELL_INFLICT_LIGHT_DAMAGE = 17100; +const int SPELL_INFLICT_MODERATE_DAMAGE = 17101; +const int SPELL_INFLICT_SERIOUS_DAMAGE = 17102; +const int SPELL_INFLICT_CRITICAL_DAMAGE = 17103; +const int SPELL_SPIRIT_WORM = 17248; + +//:: Races of Eberron +const int SPELL_MASS_REPAIR_LIGHT_DAMAGE = 17105; +const int SPELL_MASS_REPAIR_MODERATE_DAMAGE = 17106; +const int SPELL_MASS_REPAIR_SERIOUS_DAMAGE = 17107; +const int SPELL_MASS_REPAIR_CRITICAL_DAMAGE = 17108; +const int SPELL_MASS_INFLICT_LIGHT_DAMAGE = 17110; +const int SPELL_MASS_INFLICT_MODERATE_DAMAGE = 17111; +const int SPELL_MASS_INFLICT_SERIOUS_DAMAGE = 17112; +const int SPELL_MASS_INFLICT_CRITICAL_DAMAGE = 17113; //:: Masters of the Wild Spells const int SPELL_FORESTFOLD = 17090; @@ -1392,6 +1414,10 @@ const int SPELL_FOT_LEONALS_ROAR = 17993; const int SPELL_FOT_LIONS_SWIFTNESS = 17994; const int SPELL_FAVORED_OF_THE_COMPANIONS = 17995; +//:: Magic Item Compendium +const int SPELL_AROMA_OF_CURDLED_DEATH = 17987; +const int SPELL_ELIXIR_OF_THE_BEETLE = 17987; + //x const int SPELL_TENSERS_FLOATING_DISK = 3849; const int SPELL_WOLFSKIN = 3850; diff --git a/src/include/prc_spellf_inc.nss b/src/include/prc_spellf_inc.nss index 1b6042f..f40f0f6 100644 --- a/src/include/prc_spellf_inc.nss +++ b/src/include/prc_spellf_inc.nss @@ -487,7 +487,8 @@ void SpellfireChargeItem(object oPC, object oItem) AddSpellfireLevels(oPC, nNewCharges - 50); nNewCharges = 50; } - SetItemCharges(oItem, nCharges + nExpend); + //SetItemCharges(oItem, nCharges + nExpend); + SetItemCharges(oItem, nNewCharges); //Assuming 50 is the maximum //refunding excess charges } diff --git a/src/include/prc_weap_apt.nss b/src/include/prc_weap_apt.nss index 8fa1043..7bda1b7 100644 --- a/src/include/prc_weap_apt.nss +++ b/src/include/prc_weap_apt.nss @@ -47,12 +47,10 @@ int GetWeaponFocusFeatItemProperty(int nFeatNumber) if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT, IP_CONST_FEAT_WEAPON_FOCUS_TRIDENT); if(nItemProperty != -1) return nItemProperty; - nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_LIGHT_LANCE, IP_CONST_FEAT_WEAPON_FOCUS_GOAD); + nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW, IP_CONST_FEAT_WEAPON_FOCUS_GOAD); if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_LIGHTBLADE); if(nItemProperty != -1) return nItemProperty; - nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW, IP_CONST_FEAT_WEAPON_FOCUS_EAGLE_CLAW); - if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_THINBLADE); if(nItemProperty != -1) return nItemProperty; nItemProperty = GetFeatItemProperty(nFeatNumber, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE, IP_CONST_FEAT_WEAPON_FOCUS_ELVEN_COURTBLADE); diff --git a/src/include/prc_x2_craft.nss b/src/include/prc_x2_craft.nss index ac1d795..c18dcc3 100644 --- a/src/include/prc_x2_craft.nss +++ b/src/include/prc_x2_craft.nss @@ -61,6 +61,7 @@ const int X2_CI_CRAFTROD_EPIC_FEAT_ID = 3490; const int X2_CI_CRAFTSTAFF_FEAT_ID = 2928; const int X2_CI_CRAFTSTAFF_EPIC_FEAT_ID = 3491; const int X2_CI_CREATEINFUSION_FEAT_ID = 25960; +const int X2_CI_CRAFTSCEPTER_FEAT_ID = 25962; const string X2_CI_BREWPOTION_NEWITEM_RESREF = "x2_it_pcpotion"; // ResRef for new potion item const string X2_CI_SCRIBESCROLL_NEWITEM_RESREF = "x2_it_pcscroll"; // ResRef for new scroll item @@ -276,6 +277,7 @@ int CIGetIsCraftFeatBaseItem(object oItem) nBt == BASE_ITEM_BLANK_WAND || nBt == BASE_ITEM_CRAFTED_ROD || nBt == BASE_ITEM_CRAFTED_STAFF || + nBt == BASE_ITEM_CRAFTED_SCEPTER || nBt == BASE_ITEM_MUNDANE_HERB) return TRUE; else @@ -302,7 +304,7 @@ object CICraftBrewPotion(object oCreator, int nSpellID ) return OBJECT_INVALID; } - /* //just a tad retarded, don't you think? other crafting feats are not similarly restricted + /* //just a tad silly, don't you think? other crafting feats are not similarly restricted //Uses per day int nUsesAllowed; @@ -529,6 +531,7 @@ object CICraftScribeScroll(object oCreator, int nSpellID) case CLASS_TYPE_WIZARD: case CLASS_TYPE_SORCERER: sClass = "Wiz_Sorc"; break; case CLASS_TYPE_CLERIC: + case CLASS_TYPE_OCULAR: case CLASS_TYPE_UR_PRIEST: sClass = "Cleric"; break; case CLASS_TYPE_PALADIN: sClass = "Paladin"; break; case CLASS_TYPE_DRUID: @@ -709,6 +712,7 @@ object CICraftScribeScroll(object oCreator, int nSpellID) return oTarget; } */ + // ----------------------------------------------------------------------------- // Returns TRUE if the player used the last spell to brew a potion // ----------------------------------------------------------------------------- @@ -864,7 +868,6 @@ These dont work as IPs since they are hardcoded */ } - // ----------------------------------------------------------------------------- // Returns TRUE if the player used the last spell to create a scroll // ----------------------------------------------------------------------------- @@ -1132,6 +1135,169 @@ These dont work as IPs since they are hardcoded */ return FALSE; } +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to craft a scepter +// ----------------------------------------------------------------------------- +int CICraftCheckCraftScepter(object oSpellTarget, object oCaster, int nSpellID = 0) +{ + + if(nSpellID == 0) nSpellID = PRCGetSpellId(); + int nCasterLevel = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + int bSuccess = TRUE; + int nCount = 0; + itemproperty ip = GetFirstItemProperty(oSpellTarget); + int nMetaMagic = PRCGetMetaMagicFeat(); + + while(GetIsItemPropertyValid(ip)) + { + if(GetItemPropertyType(ip) == ITEM_PROPERTY_CAST_SPELL) + nCount++; + ip = GetNextItemProperty(oSpellTarget); + } + if(nCount >= 2) //:: Scepters are limited to two spells + { + FloatingTextStringOnCreature("* Failure - Too many castspell itemproperties *", oCaster); + return TRUE; + } + if(!GetHasFeat(X2_CI_CRAFTSCEPTER_FEAT_ID, oCaster)) + { + FloatingTextStrRefOnCreature(40487, oCaster); // Item Creation Failed - Don't know how to create that type of item + return TRUE; // tried item creation but do not know how to do it + } + if(CIGetIsSpellRestrictedFromCraftFeat(nSpellID, X2_CI_CRAFTSCEPTER_FEAT_ID)) + { + FloatingTextStrRefOnCreature(16829169, oCaster); // can not be used with this feat + return TRUE; + } + + // Get the base spell level (circle) before metamagic adjustments + int nBaseLevel = CIGetSpellInnateLevel(nSpellID, TRUE); + + // Check if spell circle is 7th level or lower + if (nBaseLevel > 7) + { + //FloatingTextStrRefOnCreature(83623, oCaster); // Spell level too high + FloatingTextStringOnCreature("* Failure - scepters can not hold spells higher than level 7", oCaster); + return TRUE; + } + + int nLevel = nBaseLevel; + + if(GetPRCSwitch(PRC_CRAFT_SCEPTER_CASTER_LEVEL)) + { + switch(nMetaMagic) + { + case METAMAGIC_EMPOWER: + nLevel += 2; + break; + case METAMAGIC_EXTEND: + nLevel += 1; + break; + case METAMAGIC_MAXIMIZE: + nLevel += 3; + break; +/* case METAMAGIC_QUICKEN: + nLevel += 1; + break; + case METAMAGIC_SILENT: + nLevel += 5; + break; + case METAMAGIC_STILL: + nLevel += 6; + break; + These dont work as IPs since they are hardcoded */ + } + } + + int nCostMod = GetPRCSwitch(PRC_X2_CRAFTSCEPTER_COSTMODIFIER); + if(!nCostMod) nCostMod = 750; + int nLvlRow = IPGetIPConstCastSpellFromSpellID(nSpellID); + int nCLevel = StringToInt(Get2DACache("iprp_spells","CasterLvl",nLvlRow)); + int nCost = CIGetCraftGPCost(nLevel, nCostMod, PRC_CRAFT_SCEPTER_CASTER_LEVEL); + + //discount for second spell + if(nCount+1 == 2) + nCost = (nCost/2); + + //takes epic xp costs into account + struct craft_cost_struct costs = GetModifiedCostsFromBase(nCost, oCaster, FEAT_CRAFT_SCEPTER, (nMetaMagic > 0)); + + if(costs.nGoldCost < 1) costs.nXPCost = 1; + if(costs.nXPCost < 1) costs.nXPCost = 1; + //if(GetGold(oCaster) < nGoldCost) // enough gold? + if (!GetHasGPToSpend(oCaster, costs.nGoldCost)) + { + FloatingTextStrRefOnCreature(3786, oCaster); // Item Creation Failed - not enough gold! + return TRUE; + } + int nHD = GetHitDice(oCaster); + int nMinXPForLevel = (nHD * (nHD - 1)) * 500; + int nNewXP = GetXP(oCaster) - costs.nXPCost; + //if (nMinXPForLevel > nNewXP || nNewXP == 0 ) + if (!GetHasXPToSpend(oCaster, costs.nXPCost)) + { + FloatingTextStrRefOnCreature(3785, oCaster); // Item Creation Failed - Not enough XP + return TRUE; + } + //check spell emulation + if(!CheckAlternativeCrafting(oCaster, nSpellID, costs)) + { + FloatingTextStringOnCreature("*Crafting failed!*", oCaster, FALSE); + return TRUE; + } + int nPropID = IPGetIPConstCastSpellFromSpellID(nSpellID); + if (nPropID == 0 && nSpellID != 0) + { + FloatingTextStrRefOnCreature(84544,oCaster); + return TRUE; + } + if (nPropID != -1) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpell(nPropID,IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE),oSpellTarget); + + if(GetPRCSwitch(PRC_CRAFT_SCEPTER_CASTER_LEVEL)) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellCasterLevel(nSpellID, nCasterLevel),oSpellTarget); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellMetamagic(nSpellID, PRCGetMetaMagicFeat()),oSpellTarget); + AddItemProperty(DURATION_TYPE_PERMANENT,ItemPropertyCastSpellDC(nSpellID, PRCGetSaveDC(PRCGetSpellTargetObject(), OBJECT_SELF)),oSpellTarget); + } + } + else + bSuccess = FALSE; + + if(bSuccess) + { + //TakeGoldFromCreature(nGoldCost, oCaster, TRUE); + //SetXP(oCaster, nNewXP); + SpendXP(oCaster, costs.nXPCost); + SpendGP(oCaster, costs.nGoldCost); + //DestroyObject (oSpellTarget); + FloatingTextStrRefOnCreature(8502, oCaster); // Item Creation successful + + //advance time here + if(!costs.nTimeCost) costs.nTimeCost = 1; + AdvanceTimeForPlayer(oCaster, RoundsToSeconds(costs.nTimeCost)); + string sName; + sName = GetName(oCaster)+"'s Magic Scepter"; + SetItemCharges(oSpellTarget, 50); + //sName = Get2DACache("spells", "Name", nID); + //sName = "Wand of "+GetStringByStrRef(StringToInt(sName)); + SetName(oSpellTarget, sName); + SetItemCursedFlag(oSpellTarget, FALSE); + SetDroppableFlag(oSpellTarget, TRUE); + return TRUE; + } + else + { + FloatingTextStrRefOnCreature(76417, oCaster); // Item Creation Failed + return TRUE; + } + return TRUE; +} + +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to craft a staff +// ----------------------------------------------------------------------------- int CICraftCheckCraftStaff(object oSpellTarget, object oCaster, int nSpellID = 0) { @@ -1280,6 +1446,9 @@ These dont work as IPs since they are hardcoded */ return TRUE; } +// ----------------------------------------------------------------------------- +// Returns TRUE if the player used the last spell to craft a rod +// ----------------------------------------------------------------------------- int CICraftCheckCraftRod(object oSpellTarget, object oCaster, int nSpellID = 0) { @@ -1426,6 +1595,7 @@ These dont work as IPs since they are hardcoded */ return TRUE; } + int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, int nSpell = 0) { if(!GetIsObjectValid(oCaster)) oCaster = OBJECT_SELF; @@ -1476,6 +1646,11 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject(); int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); + +//:: Check for Inscribe Epic Runes and cap CL at 20 if it doesn't exist. + int bEpicRunes = GetHasFeat(EPIC_FEAT_INSCRIBE_EPIC_RUNES, oCaster); + if (!bEpicRunes) { if(nCaster > 20) nCaster = 20; } + int nDC = PRCGetSaveDC(oTarget, oCaster); if(!nSpell) nSpell = PRCGetSpellId(); int nSpellLevel = 0; @@ -1498,6 +1673,7 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI // Minimum level. if (nSpellLevel == 0) nSpellLevel = 1; + // This will be modified with Runecaster code later. int nCharges = 1; if (GetLocalInt(oCaster, "RuneCharges")) nCharges = nCount; @@ -1606,9 +1782,14 @@ int InscribeRune(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALI // If they have this active, the bonuses are already added, so skip // If they don't, add the bonuses down below if(GetHasSpellEffect(SPELL_RUNE_CHANT)) - nRuneChant = 0; + nRuneChant = 0; - itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, PRCGetCasterLevel()); + //:: Check for Inscribe Epic Runes and cap CL at 20 if it doesn't exist. + nCaster = PRCGetCasterLevel(); + + if (!bEpicRunes) { if(nCaster > 20) nCaster = 20; } + + itemproperty ipLevel = ItemPropertyCastSpellCasterLevel(nSpell, nCaster); AddItemProperty(DURATION_TYPE_PERMANENT,ipLevel,oRune); itemproperty ipMeta = ItemPropertyCastSpellMetamagic(nSpell, PRCGetMetaMagicFeat()); AddItemProperty(DURATION_TYPE_PERMANENT,ipMeta,oRune); @@ -1715,13 +1896,23 @@ int AttuneGem(object oTarget = OBJECT_INVALID, object oCaster = OBJECT_INVALID, } // oTarget here should be the gem. If it's not, fail. if(!GetIsObjectValid(oTarget)) oTarget = PRCGetSpellTargetObject(); - // Only accepts bioware gems - if (GetStringLeft(GetResRef(oTarget), 5) == "it_gem") + // Only accepts bioware gems & Craftable Natural Resources gems, but not gem dust. + int bIsBioGem = (GetStringLeft(GetResRef(oTarget), 5) == "it_gem"); + int bIsCNRGem = (GetStringLeft(GetResRef(oTarget), 6) == "cnrgem"); + int bIsDust = (GetStringLeft(GetResRef(oTarget), 10) == "cnrgemdust"); + + if (!(bIsBioGem || bIsCNRGem) || bIsDust) + { + FloatingTextStringOnCreature("Spell target is not a valid gem.", oCaster, FALSE); + return TRUE; + } + +/* if ((GetStringLeft(GetResRef(oTarget), 5) == "it_gem") || (GetStringLeft(GetResRef(oTarget), 6) == "cnrgem") && (GetStringLeft(GetResRef(oTarget), 10) != "cnrgemdust")) { FloatingTextStringOnCreature("Spell target is not a valid gem.", oCaster, FALSE); // And out we go return TRUE; - } + } */ int nCaster = GetAlternativeCasterLevel(oCaster, PRCGetCasterLevel(oCaster)); int nDC = PRCGetSaveDC(oTarget, oCaster); @@ -2117,6 +2308,13 @@ int CIGetSpellWasUsedForItemCreation(object oSpellTarget) // ------------------------------------------------- nRet = CICraftCheckCraftStaff(oSpellTarget,oCaster); break; + + case BASE_ITEM_CRAFTED_SCEPTER : + // ------------------------------------------------- + // Craft Scepter + // ------------------------------------------------- + nRet = CICraftCheckCraftScepter(oSpellTarget,oCaster); + break; case BASE_ITEM_MUNDANE_HERB : // ------------------------------------------------- @@ -2916,7 +3114,12 @@ int GetMagicalArtisanFeat(int nCraftingFeat) { nReturn = FEAT_MAGICAL_ARTISAN_CREATE_INFUSION; break; - } + } + case FEAT_CRAFT_SCEPTER: + { + nReturn = FEAT_MAGICAL_ARTISAN_CRAFT_SCEPTER; + break; + } default: { if(DEBUG) DoDebug("GetMagicalArtisanFeat: invalid crafting feat"); diff --git a/src/include/prc_x2_itemprop.nss b/src/include/prc_x2_itemprop.nss index 17885cd..1abf20c 100644 --- a/src/include/prc_x2_itemprop.nss +++ b/src/include/prc_x2_itemprop.nss @@ -20,9 +20,6 @@ //:: Last Update: 2003-10-07 //::////////////////////////////////////////////// -//:: Test void -//:: void main (){} - //Changed by primogenitor to include CEP itemtypes // * The tag of the ip work container, a placeable which has to be set into each @@ -697,6 +694,7 @@ if(nItem == BASE_ITEM_BASTARDSWORD || nItem == BASE_ITEM_SICKLE || nItem == BASE_ITEM_TWOBLADEDSWORD || nItem == BASE_ITEM_CLUB + || nItem == BASE_ITEM_CRAFTED_SCEPTER || nItem == BASE_ITEM_DAGGER || nItem == BASE_ITEM_DIREMACE || nItem == BASE_ITEM_HEAVYFLAIL @@ -729,6 +727,7 @@ if(nItem == BASE_ITEM_BASTARDSWORD || nItem == BASE_ITEM_ELVEN_THINBLADE || nItem == BASE_ITEM_ELVEN_COURTBLADE || nItem == BASE_ITEM_CRAFTED_STAFF + || nItem == BASE_ITEM_CRAFTED_SCEPTER || nItem == 300 //CEP Trident || nItem == 303 //CEP Sai || nItem == 304 //CEP nunchaku @@ -1612,31 +1611,140 @@ int IPGetDamageBonusConstantFromNumber(int nNumber) // oOld - Item equipped before polymorphing (source for item props) // oNew - Item equipped after polymorphing (target for item props) // bWeapon - Must be set TRUE when oOld is a weapon. -// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- void IPWildShapeCopyItemProperties(object oOld, object oNew, int bWeapon = FALSE) { - if (GetIsObjectValid(oOld) && GetIsObjectValid(oNew)) - { - itemproperty ip = GetFirstItemProperty(oOld); - while (GetIsItemPropertyValid(ip)) - { - if (bWeapon) - { - if (GetWeaponRanged(oOld) == GetWeaponRanged(oNew) ) - { - AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); - } - } - else - { - AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); - } - ip = GetNextItemProperty(oOld); + // Invalid source/target + if (!GetIsObjectValid(oOld) || !GetIsObjectValid(oNew)) + return; + + // Determine possessor + object oPC = GetItemPossessor(oOld); + if (!GetIsObjectValid(oPC)) + oPC = GetItemPossessor(oNew); - } - } + if (!GetIsObjectValid(oPC)) + { + if (DEBUG) DoDebug("IPWS: Unable to determine item possessor"); + return; + } + + // Determine glove state once + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + + // Weapon ranged mismatch = do nothing (intent is no partial copy) + if (bWeapon && GetWeaponRanged(oOld) != GetWeaponRanged(oNew)) + { + if (DEBUG) DoDebug("IPWS: Weapon ranged mismatch — skipping all IP copy"); + return; + } + + // Begin property copy + itemproperty ip = GetFirstItemProperty(oOld); + while (GetIsItemPropertyValid(ip)) + { + int nType = GetItemPropertyType(ip); + + // If copying from gloves and monk gloves are active + if (bMonkGloves + && (nType == ITEM_PROPERTY_DAMAGE_BONUS + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP)) + { + // Always apply glove damage IPs + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + ip = GetNextItemProperty(oOld); + continue; + } + + // Normal weapon pass + if (bWeapon) + { + // If monk gloves active ? skip ALL weapon damage IPs + if (bMonkGloves + && (nType == ITEM_PROPERTY_DAMAGE_BONUS + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP)) + { + ip = GetNextItemProperty(oOld); + continue; + } + + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + } + else + { + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + } + + ip = GetNextItemProperty(oOld); + } } + +/* // ---------------------------------------------------------------------------- +// GZ, Sept. 30 2003 +// Special Version of Copy Item Properties for use with greater wild shape +// oOld - Item equipped before polymorphing (source for item props) +// oNew - Item equipped after polymorphing (target for item props) +// bWeapon - Must be set TRUE when oOld is a weapon. +// ---------------------------------------------------------------------------- +void IPWildShapeCopyItemProperties(object oOld, object oNew, int bWeapon = FALSE) +{ + if (!GetIsObjectValid(oOld) || !GetIsObjectValid(oNew)) + return; + + object oPC = GetItemPossessor(oOld); + if (!GetIsObjectValid(oPC)) + { + oPC = GetItemPossessor(oNew); + } + if (!GetIsObjectValid(oPC)) + { + if (DEBUG) DoDebug("IPWS: Unable to determine item possessor"); + return; + } + + int bMonkGloves = GetLocalInt(oPC, "WEARING_MONK_GLOVES"); + + itemproperty ip = GetFirstItemProperty(oOld); + while (GetIsItemPropertyValid(ip)) + { + if (bWeapon) + { + // Gloves override weapon damage — skip weapon damage properties + if (bMonkGloves) + { + int nType = GetItemPropertyType(ip); + + // skip damage props + if (nType == ITEM_PROPERTY_DAMAGE_BONUS + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP + || nType == ITEM_PROPERTY_DAMAGE_BONUS_VS_ALIGNMENT_GROUP) + { + if (DEBUG) DoDebug("IPWS: SKIPPED weapon damage IP"); + } + else + { + if (DEBUG) DoDebug("IPWS: Applied non-damage weapon IP"); + AddItemProperty(DURATION_TYPE_PERMANENT, ip, oNew); + } + + } + else if (GetWeaponRanged(oOld) == GetWeaponRanged(oNew) ) + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); + } + } + else + { + AddItemProperty(DURATION_TYPE_PERMANENT,ip,oNew); + } + + ip = GetNextItemProperty(oOld); + } +} */ + // ---------------------------------------------------------------------------- // Returns the current enhancement bonus of a weapon (+1 to +20), 0 if there is // no enhancement bonus. You can test for a specific type of enhancement bonus @@ -2018,4 +2126,6 @@ int IPOnHitSaveDC(int nSaveDC) if (nSaveDC > 26) nIPBonus = IP_CONST_ONHIT_SAVEDC_26; return nIPBonus; -} */ \ No newline at end of file +} */ + +//:: void main(){} \ No newline at end of file diff --git a/src/include/prcsp_archmaginc.nss b/src/include/prcsp_archmaginc.nss index 4287a19..72e5d44 100644 --- a/src/include/prcsp_archmaginc.nss +++ b/src/include/prcsp_archmaginc.nss @@ -73,6 +73,7 @@ void SetMasteryOfElements(); //#include "lookup_2da_spell" #include "prcsp_reputation" +#include "prc_inc_core" //#include "prc_inc_spells" diff --git a/src/include/psi_inc_ac_spawn.nss b/src/include/psi_inc_ac_spawn.nss index 5b3947e..43765dd 100644 --- a/src/include/psi_inc_ac_spawn.nss +++ b/src/include/psi_inc_ac_spawn.nss @@ -11,6 +11,7 @@ #include "prc_ipfeat_const" #include "prc_feat_const" #include "inc_vfx_const" +#include "prc_inc_nwscript" ////////////////////////////////////////////////// diff --git a/src/include/psi_inc_core.nss b/src/include/psi_inc_core.nss index 9083bee..b4ca245 100644 --- a/src/include/psi_inc_core.nss +++ b/src/include/psi_inc_core.nss @@ -41,6 +41,8 @@ const int POWER_LIST_WARMIND = CLASS_TYPE_WARMIND; /* Function prototypes */ ////////////////////////////////////////////////// +int IsHiddenTalent(object oPC = OBJECT_SELF); + /** * Attempts to use psionic focus. If the creature was focused, it * loses the focus. If it has Epic Psionic Focus feats, it will @@ -520,9 +522,9 @@ void GainPsionicFocus(object oGainee = OBJECT_SELF) { int nPsySneak = 1; if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_2d6, oGainee)) - nPsySneak += 2; + nPsySneak += 1; if(GetHasFeat(FEAT_PSY_SNEAK_ATTACK_3d6, oGainee)) - nPsySneak += 3; + nPsySneak += 1; SetLocalInt(oGainee, "PsyRogueSneak",nPsySneak); DelayCommand(0.1, ExecuteScript("prc_sneak_att", oGainee)); @@ -786,69 +788,12 @@ int GetIsPsionicCharacter(object oCreature) GetHasFeat(FEAT_KALASHTAR_PP, oCreature) || GetHasFeat(FEAT_NATPSIONIC_1, oCreature) || GetHasFeat(FEAT_NATPSIONIC_2, oCreature) || - GetHasFeat(FEAT_NATPSIONIC_3, oCreature) + GetHasFeat(FEAT_NATPSIONIC_3, oCreature) || + IsHiddenTalent(oCreature) // Racial psionicity signifying feats go here ); } -int IsHiddenTalent(object oPC = OBJECT_SELF) -{ - if (GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC) || - //GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC) || - GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) - { - return TRUE; - } - else - { - return FALSE; - } -} - - void LocalCleanExtraFists(object oCreature) { int iIsCWeap, iIsEquip; @@ -985,6 +930,48 @@ int PracticedManifesting(object oManifester, int iManifestingClass, int iManifes int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVALID, int nMaxPowerLevel = FALSE) { + // Handle POWER_LIST_MISC (Hidden Talent) powers + // Check if this is a power list call + int nPowerType = GetLocalInt(oManifester, "PRC_UsePowerList"); + + if(nSpecificClass == CLASS_TYPE_INVALID && nPowerType == POWER_LIST_MISC) + { + if(DEBUG) DoDebug("psi_inc_core >> GetManifesterLevel: CLASS_TYPE_INVALID + POWER_LIST_MISC found!"); + // Check if character has psionic class levels + int nPsionLevel = GetLevelByClass(CLASS_TYPE_PSION, oManifester); + int nPsywarLevel = GetLevelByClass(CLASS_TYPE_PSYWAR, oManifester); + int nWilderLevel = GetLevelByClass(CLASS_TYPE_WILDER, oManifester); + int nWarmindLevel = GetLevelByClass(CLASS_TYPE_WARMIND, oManifester); + int nFistOfZuokenLevel = GetLevelByClass(CLASS_TYPE_FIST_OF_ZUOKEN, oManifester); + int nPsychicRogueLevel = GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oManifester); + + // If no psionic levels, use Charisma-based calculation (treat as 1st level) + if(nPsionLevel + nPsywarLevel + nWilderLevel + nWarmindLevel + + nFistOfZuokenLevel + nPsychicRogueLevel == 0) + { + // Hidden Talent: considered 1st-level manifester, but must have CHA 11+ + if(DEBUG) DoDebug("psi_inc_core >> GetManifesterLevel: Hidden Talent found!"); + if(GetAbilityScore(oManifester, ABILITY_CHARISMA) >= 11) + return 1; + else + return 0; // Cannot manifest without CHA 11+ + } + + if(DEBUG) DoDebug("psi_inc_core >> GetManifesterLevel: nSpecificClass=" + IntToString(nSpecificClass) + + ", nPowerType=" + IntToString(nPowerType)); + + // Has psionic levels - return highest manifester level + int nHighest = 0; + if(nPsionLevel > 0) nHighest = GetManifesterLevel(oManifester, CLASS_TYPE_PSION); + if(nPsywarLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_PSYWAR)); + if(nWilderLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_WILDER)); + if(nWarmindLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_WARMIND)); + if(nFistOfZuokenLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_FIST_OF_ZUOKEN)); + if(nPsychicRogueLevel > 0) nHighest = PRCMax(nHighest, GetManifesterLevel(oManifester, CLASS_TYPE_PSYCHIC_ROGUE)); + + return nHighest; + } + int nLevel; int nAdjust = GetLocalInt(oManifester, PRC_CASTERLEVEL_ADJUSTMENT); nAdjust -= GetLocalInt(oManifester, "WoLManifPenalty"); @@ -1049,17 +1036,27 @@ int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVAL DelayCommand(1.0, DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE)); nLevel = GetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE); - } - else if(GetManifestingClass(oManifester) != CLASS_TYPE_INVALID) - { - //Gets the manifesting class - int nManifestingClass = GetManifestingClass(oManifester); - if(DEBUG) DoDebug("Manifesting Class #2: " + IntToString(nManifestingClass), oManifester); - nLevel = GetLevelByClass(nManifestingClass, oManifester); - // Add levels from +ML PrCs only for the first manifesting class - nLevel += GetPsionicPRCLevels(oManifester, nManifestingClass); - //nLevel += nManifestingClass == GetPrimaryPsionicClass(oManifester) ? GetPsionicPRCLevels(oManifester) : 0; - + } + else if(GetManifestingClass(oManifester) != CLASS_TYPE_INVALID) + { + //Gets the manifesting class + int nManifestingClass = GetManifestingClass(oManifester); + if(DEBUG) DoDebug("Manifesting Class #2: " + IntToString(nManifestingClass), oManifester); + + nLevel = GetLevelByClass(nManifestingClass, oManifester); + // Add levels from +ML PrCs only for the first manifesting class + nLevel += GetPsionicPRCLevels(oManifester, nManifestingClass); + + // CHECK: If this is Hidden Talent and character has no levels, set to 1 + if(nLevel == 0 && GetLocalInt(oManifester, "PRC_UsePowerList") == TRUE && + GetLocalInt(oManifester, "PRC_PowerListType") == POWER_LIST_MISC) + { + if(GetAbilityScore(oManifester, ABILITY_CHARISMA) >= 11) + { + if(DEBUG) DoDebug("GetManifesterLevel: Hidden Talent with no psionic levels, returning 1"); + nLevel = 1; + } + } // Psionic vestiges are tucked in here to override things. // This assumes that there will never be a psion with this spell effect manifesting things if (nManifestingClass == CLASS_TYPE_PSION && GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !nMaxPowerLevel) @@ -1085,7 +1082,37 @@ int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVAL // if(DEBUG) DoDebug("Level gotten via GetLevelByClass: " + IntToString(nLevel), oManifester); } - // If you have a primary psionic class and no manifester level yet, get levels based on that + // If you have a primary psionic class and no manifester level yet, get levels based on that + if (GetPrimaryPsionicClass(oManifester) && nLevel == 0) + { + int nClass = GetPrimaryPsionicClass(oManifester); + nLevel = GetLevelByClass(nClass, oManifester); + nLevel += GetPsionicPRCLevels(oManifester, nClass); + nLevel += PracticedManifesting(oManifester, nClass, nLevel); + } + + // If everything else fails, check for Hidden Talent before returning 0 + if(nLevel == 0) + { + // Check if this is a Hidden Talent power + if(GetLocalInt(oManifester, "PRC_UsePowerList") == POWER_LIST_MISC) + { + // Hidden Talent: manifester level is 1 if they have CHA 11+ + if(GetAbilityScore(oManifester, ABILITY_CHARISMA) >= 11) + { + if(DEBUG) DoDebug("GetManifesterLevel: Hidden Talent character, returning level 1"); + return 1; + } + } + + if(DEBUG) DoDebug("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + //else WriteTimestampedLogEntry("Failed to get manifester level for creature " + DebugObject2Str(oManifester) + ", using first class slot"); + + return 0; + } + + +/* // If you have a primary psionic class and no manifester level yet, get levels based on that if (GetPrimaryPsionicClass(oManifester) && nLevel == 0) { int nClass = GetPrimaryPsionicClass(oManifester); @@ -1102,7 +1129,7 @@ int GetManifesterLevel(object oManifester, int nSpecificClass = CLASS_TYPE_INVAL return 0; } - + */ // The bonuses inside only apply to normal manifestation if(!GetLocalInt(oManifester, PRC_IS_PSILIKE)) @@ -1665,4 +1692,225 @@ int GetMaxPowerLevel(object oManifester) int nMax = StringToInt(Get2DACache(sFile, "MaxPowerLevel", nLevel)); if (DEBUG) DoDebug("GetMaxPowerLevel is "+IntToString(nMax)); return nMax; -} \ No newline at end of file +} + +////////////////////////////////////////////////////// +/* START HIDDEN TALENT */ +////////////////////////////////////////////////////// + +int IsHiddenTalent(object oPC = OBJECT_SELF) +{ + if (GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC) || + //GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC) || + GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) + { + return TRUE; + } + else + { + return FALSE; + } +} + +int GetHiddenTalentPowerFromFeat(int nFeatID) +{ + // Map Hidden Talent feats to their corresponding power IDs + // Using the same mappings as GetIsHiddenTalentPower() + if(nFeatID == FEAT_HIDDEN_TALENT_BIOFEEDBACK) return POWER_BIOFEEDBACK; + if(nFeatID == FEAT_HIDDEN_TALENT_BITE_WOLF) return POWER_BITE_WOLF; + if(nFeatID == FEAT_HIDDEN_TALENT_BOLT) return POWER_BOLT; + if(nFeatID == FEAT_HIDDEN_TALENT_BURST) return POWER_BURST; + if(nFeatID == FEAT_HIDDEN_TALENT_CALLTOMIND) return POWER_CALLTOMIND; + if(nFeatID == FEAT_HIDDEN_TALENT_CALL_WEAPONRY) return POWER_CALL_WEAPONRY; + if(nFeatID == FEAT_HIDDEN_TALENT_CHAMELEON) return POWER_CHAMELEON; + if(nFeatID == FEAT_HIDDEN_TALENT_CLAWS_BEAST) return POWER_CLAWS_BEAST; + if(nFeatID == FEAT_HIDDEN_TALENT_COMPRESSION) return POWER_COMPRESSION; + if(nFeatID == FEAT_HIDDEN_TALENT_CONCEALTHOUGHT) return POWER_CONCEALTHOUGHT; + if(nFeatID == FEAT_HIDDEN_TALENT_CREATESOUND) return POWER_CREATESOUND; + if(nFeatID == FEAT_HIDDEN_TALENT_CRYSTALSHARD) return POWER_CRYSTALSHARD; + if(nFeatID == FEAT_HIDDEN_TALENT_DAZE) return POWER_DAZE; + if(nFeatID == FEAT_HIDDEN_TALENT_DECELERATION) return POWER_DECELERATION; + if(nFeatID == FEAT_HIDDEN_TALENT_DEFPRECOG) return POWER_DEFPRECOG; + if(nFeatID == FEAT_HIDDEN_TALENT_DEMORALIZE) return POWER_DEMORALIZE; + if(nFeatID == FEAT_HIDDEN_TALENT_DISABLE) return POWER_DISABLE; + if(nFeatID == FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH)return POWER_DISSIPATINGTOUCH; + if(nFeatID == FEAT_HIDDEN_TALENT_DISTRACT) return POWER_DISTRACT; + if(nFeatID == FEAT_HIDDEN_TALENT_ELFSIGHT) return POWER_ELFSIGHT; + if(nFeatID == FEAT_HIDDEN_TALENT_EMPATHY) return POWER_EMPATHY; + if(nFeatID == FEAT_HIDDEN_TALENT_EMPTYMIND) return POWER_EMPTYMIND; + if(nFeatID == FEAT_HIDDEN_TALENT_ENTANGLE) return POWER_ENTANGLE; + if(nFeatID == FEAT_HIDDEN_TALENT_EXPANSION) return POWER_EXPANSION; + if(nFeatID == FEAT_HIDDEN_TALENT_FARHAND) return POWER_FARHAND; + if(nFeatID == FEAT_HIDDEN_TALENT_FORCESCREEN) return POWER_FORCESCREEN; + if(nFeatID == FEAT_HIDDEN_TALENT_GREASE) return POWER_GREASE; + if(nFeatID == FEAT_HIDDEN_TALENT_HAMMER) return POWER_HAMMER; + if(nFeatID == FEAT_HIDDEN_TALENT_INERTIALARMOUR) return POWER_INERTIALARMOUR; + if(nFeatID == FEAT_HIDDEN_TALENT_MATTERAGITATION) return POWER_MATTERAGITATION; + if(nFeatID == FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW) return POWER_METAPHYSICAL_CLAW; + if(nFeatID == FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON) return POWER_METAPHYSICAL_WEAPON; + if(nFeatID == FEAT_HIDDEN_TALENT_MINDTHRUST) return POWER_MINDTHRUST; + if(nFeatID == FEAT_HIDDEN_TALENT_MYLIGHT) return POWER_MYLIGHT; + if(nFeatID == FEAT_HIDDEN_TALENT_OFFPRECOG) return POWER_OFFPRECOG; + if(nFeatID == FEAT_HIDDEN_TALENT_OFFPRESC) return POWER_OFFPRESC; + if(nFeatID == FEAT_HIDDEN_TALENT_PREVENOM) return POWER_PREVENOM; + if(nFeatID == FEAT_HIDDEN_TALENT_PREVENOM_WEAPON) return POWER_PREVENOM_WEAPON; + if(nFeatID == FEAT_HIDDEN_TALENT_SKATE) return POWER_SKATE; + if(nFeatID == FEAT_HIDDEN_TALENT_STOMP) return POWER_STOMP; + if(nFeatID == FEAT_HIDDEN_TALENT_SYNESTHETE) return POWER_SYNESTHETE; + if(nFeatID == FEAT_HIDDEN_TALENT_TELEMPATHICPRO) return POWER_TELEMPATHICPRO; + if(nFeatID == FEAT_HIDDEN_TALENT_THICKSKIN) return POWER_THICKSKIN; + if(nFeatID == FEAT_HIDDEN_TALENT_VIGOR) return POWER_VIGOR; + if(nFeatID == FEAT_HIDDEN_TALENT_GRIP_IRON) return POWER_GRIP_IRON; + + return -1; // Not found +} + +int GetHiddenTalentCount(object oPC = OBJECT_SELF) +{ + int nCount = 0; + + if (GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC)) nCount++; + //if (GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC))nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC)) nCount++; + if (GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) nCount++; + + return nCount; +} + +int GetIsHiddenTalentPower(object oPC, int nPower) +{ + // Check each Hidden Talent feat to see if it grants this power + if(nPower == POWER_BIOFEEDBACK && GetHasFeat(FEAT_HIDDEN_TALENT_BIOFEEDBACK, oPC)) return TRUE; + if(nPower == POWER_BITE_WOLF && GetHasFeat(FEAT_HIDDEN_TALENT_BITE_WOLF, oPC)) return TRUE; + if(nPower == POWER_BOLT && GetHasFeat(FEAT_HIDDEN_TALENT_BOLT, oPC)) return TRUE; + if(nPower == POWER_BURST && GetHasFeat(FEAT_HIDDEN_TALENT_BURST, oPC)) return TRUE; + if(nPower == POWER_CALLTOMIND && GetHasFeat(FEAT_HIDDEN_TALENT_CALLTOMIND, oPC)) return TRUE; + if(nPower == POWER_CALL_WEAPONRY && GetHasFeat(FEAT_HIDDEN_TALENT_CALL_WEAPONRY, oPC)) return TRUE; + if(nPower == POWER_CHAMELEON && GetHasFeat(FEAT_HIDDEN_TALENT_CHAMELEON, oPC)) return TRUE; + if(nPower == POWER_CLAWS_BEAST && GetHasFeat(FEAT_HIDDEN_TALENT_CLAWS_BEAST, oPC)) return TRUE; + if(nPower == POWER_COMPRESSION && GetHasFeat(FEAT_HIDDEN_TALENT_COMPRESSION, oPC)) return TRUE; + if(nPower == POWER_CONCEALTHOUGHT && GetHasFeat(FEAT_HIDDEN_TALENT_CONCEALTHOUGHT, oPC)) return TRUE; + if(nPower == POWER_CREATESOUND && GetHasFeat(FEAT_HIDDEN_TALENT_CREATESOUND, oPC)) return TRUE; + if(nPower == POWER_CRYSTALSHARD && GetHasFeat(FEAT_HIDDEN_TALENT_CRYSTALSHARD, oPC)) return TRUE; + if(nPower == POWER_DAZE && GetHasFeat(FEAT_HIDDEN_TALENT_DAZE, oPC)) return TRUE; + if(nPower == POWER_DECELERATION && GetHasFeat(FEAT_HIDDEN_TALENT_DECELERATION, oPC)) return TRUE; + if(nPower == POWER_DEFPRECOG && GetHasFeat(FEAT_HIDDEN_TALENT_DEFPRECOG, oPC)) return TRUE; + if(nPower == POWER_DEMORALIZE && GetHasFeat(FEAT_HIDDEN_TALENT_DEMORALIZE, oPC)) return TRUE; + if(nPower == POWER_DISABLE && GetHasFeat(FEAT_HIDDEN_TALENT_DISABLE, oPC)) return TRUE; + if(nPower == POWER_DISSIPATINGTOUCH && GetHasFeat(FEAT_HIDDEN_TALENT_DISSIPATINGTOUCH, oPC)) return TRUE; + if(nPower == POWER_DISTRACT && GetHasFeat(FEAT_HIDDEN_TALENT_DISTRACT, oPC)) return TRUE; + if(nPower == POWER_ELFSIGHT && GetHasFeat(FEAT_HIDDEN_TALENT_ELFSIGHT, oPC)) return TRUE; + if(nPower == POWER_EMPATHY && GetHasFeat(FEAT_HIDDEN_TALENT_EMPATHY, oPC)) return TRUE; + if(nPower == POWER_EMPTYMIND && GetHasFeat(FEAT_HIDDEN_TALENT_EMPTYMIND, oPC)) return TRUE; + //if(nPower == POWER_ENERGYRAY && GetHasFeat(FEAT_HIDDEN_TALENT_ENERGYRAY, oPC)) return TRUE; + if(nPower == POWER_ENTANGLE && GetHasFeat(FEAT_HIDDEN_TALENT_ENTANGLE, oPC)) return TRUE; + if(nPower == POWER_EXPANSION && GetHasFeat(FEAT_HIDDEN_TALENT_EXPANSION, oPC)) return TRUE; + if(nPower == POWER_FARHAND && GetHasFeat(FEAT_HIDDEN_TALENT_FARHAND, oPC)) return TRUE; + if(nPower == POWER_FORCESCREEN && GetHasFeat(FEAT_HIDDEN_TALENT_FORCESCREEN, oPC)) return TRUE; + if(nPower == POWER_GREASE && GetHasFeat(FEAT_HIDDEN_TALENT_GREASE, oPC)) return TRUE; + if(nPower == POWER_HAMMER && GetHasFeat(FEAT_HIDDEN_TALENT_HAMMER, oPC)) return TRUE; + if(nPower == POWER_INERTIALARMOUR && GetHasFeat(FEAT_HIDDEN_TALENT_INERTIALARMOUR, oPC)) return TRUE; + if(nPower == POWER_MATTERAGITATION && GetHasFeat(FEAT_HIDDEN_TALENT_MATTERAGITATION, oPC)) return TRUE; + if(nPower == POWER_METAPHYSICAL_CLAW && GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_CLAW, oPC)) return TRUE; + if(nPower == POWER_METAPHYSICAL_WEAPON && GetHasFeat(FEAT_HIDDEN_TALENT_METAPHYSICAL_WEAPON, oPC)) return TRUE; + if(nPower == POWER_MINDTHRUST && GetHasFeat(FEAT_HIDDEN_TALENT_MINDTHRUST, oPC)) return TRUE; + if(nPower == POWER_MYLIGHT && GetHasFeat(FEAT_HIDDEN_TALENT_MYLIGHT, oPC)) return TRUE; + if(nPower == POWER_OFFPRECOG && GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRECOG, oPC)) return TRUE; + if(nPower == POWER_OFFPRESC && GetHasFeat(FEAT_HIDDEN_TALENT_OFFPRESC, oPC)) return TRUE; + if(nPower == POWER_PREVENOM && GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM, oPC)) return TRUE; + if(nPower == POWER_PREVENOM_WEAPON && GetHasFeat(FEAT_HIDDEN_TALENT_PREVENOM_WEAPON, oPC)) return TRUE; + if(nPower == POWER_SKATE && GetHasFeat(FEAT_HIDDEN_TALENT_SKATE, oPC)) return TRUE; + if(nPower == POWER_STOMP && GetHasFeat(FEAT_HIDDEN_TALENT_STOMP, oPC)) return TRUE; + if(nPower == POWER_SYNESTHETE && GetHasFeat(FEAT_HIDDEN_TALENT_SYNESTHETE, oPC)) return TRUE; + if(nPower == POWER_TELEMPATHICPRO && GetHasFeat(FEAT_HIDDEN_TALENT_TELEMPATHICPRO, oPC)) return TRUE; + if(nPower == POWER_THICKSKIN && GetHasFeat(FEAT_HIDDEN_TALENT_THICKSKIN, oPC)) return TRUE; + if(nPower == POWER_VIGOR && GetHasFeat(FEAT_HIDDEN_TALENT_VIGOR, oPC)) return TRUE; + if(nPower == POWER_GRIP_IRON && GetHasFeat(FEAT_HIDDEN_TALENT_GRIP_IRON, oPC)) return TRUE; + + return FALSE; +} diff --git a/src/include/psi_inc_metapsi.nss b/src/include/psi_inc_metapsi.nss index c005f0c..3148718 100644 --- a/src/include/psi_inc_metapsi.nss +++ b/src/include/psi_inc_metapsi.nss @@ -110,6 +110,7 @@ object GetSplitPsionicRayTarget(struct manifestation manif, object oPrimaryTarge ////////////////////////////////////////////////// #include "psi_inc_core" +#include "psi_inc_psifunc" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/psi_inc_powknown.nss b/src/include/psi_inc_powknown.nss index 51e304f..20c1951 100644 --- a/src/include/psi_inc_powknown.nss +++ b/src/include/psi_inc_powknown.nss @@ -572,7 +572,11 @@ int GetMaxPowerCount(object oCreature, int nList) int GetHasPower(int nPower, object oCreature = OBJECT_SELF) { - if((GetLevelByClass(CLASS_TYPE_PSION, oCreature) + // Check MISC list first (for Hidden Talent and similar feats) + if(GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_INVALID), oCreature)) + return TRUE; + + if((GetLevelByClass(CLASS_TYPE_PSION, oCreature) && GetHasFeat(GetClassFeatFromPower(nPower, CLASS_TYPE_PSION), oCreature) ) || (GetLevelByClass(CLASS_TYPE_PSYWAR, oCreature) diff --git a/src/include/psi_inc_ppoints.nss b/src/include/psi_inc_ppoints.nss index a98f0fb..ca66fba 100644 --- a/src/include/psi_inc_ppoints.nss +++ b/src/include/psi_inc_ppoints.nss @@ -93,9 +93,8 @@ void GainPowerPoints(object oChar, int nGain, int bCanExceedMax = FALSE, int bIn * @param bInform If TRUE, runs TellCharacterPowerPointStatus() on oChar * after making the modification. */ -/* void GainTemporaryPowerPoints(object oChar, int nGain, float fDuration, int bInform = TRUE); -*/ + /** * Decreases the character's current power point count by up to the given * amount, limited to not going below 0. @@ -138,8 +137,12 @@ int _GetFeatBonusPP(object oChar) { int nBonusPP = 0; -//:: Wild Talent & Hidden Talents - if(GetHasFeat(FEAT_WILD_TALENT, oChar) || IsHiddenTalent()) +//:: Wild Talent + if(GetHasFeat(FEAT_WILD_TALENT, oChar)) + nBonusPP += 2; + +//:: Hidden Talent + if(GetHasFeat(FEAT_HIDDEN_TALENT, oChar)) nBonusPP += 2; //:: Psionic Feats diff --git a/src/include/psi_inc_psicraft.nss b/src/include/psi_inc_psicraft.nss index 058ba59..e5e244d 100644 --- a/src/include/psi_inc_psicraft.nss +++ b/src/include/psi_inc_psicraft.nss @@ -40,7 +40,8 @@ void IdentifyPower(object oManifester, int nPowerId); // Always access via psi_inc_psifunc. -//#include "psi_inc_core" +#include "psi_inc_core" +#include "inc_2dacache" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/psi_inc_psifunc.nss b/src/include/psi_inc_psifunc.nss index 5e96a40..c35b122 100644 --- a/src/include/psi_inc_psifunc.nss +++ b/src/include/psi_inc_psifunc.nss @@ -407,6 +407,8 @@ void _CleanManifestationVariables(object oManifester) DeleteLocalInt(oManifester, PRC_POWER_LEVEL); DeleteLocalInt(oManifester, PRC_IS_PSILIKE); DeleteLocalInt(oManifester, PRC_AUGMENT_OVERRIDE); + DeleteLocalInt(oManifester, "PRC_UsePowerList"); + DeleteLocalInt(oManifester, "PRC_PowerListType"); } /** Internal function. @@ -692,10 +694,28 @@ void _UsePowerAux(object oManifester, object oMfToken, int nSpellId, struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags) { - /* Get some data */ + //:: Handle Hidden Talent + int nSpellID = PRCGetSpellId(); + int bIsHiddenTalent = GetIsHiddenTalentPower(oManifester, nSpellID); + if(bIsHiddenTalent) + { + SetLocalInt(oManifester, "PRC_UsePowerList", TRUE); + SetLocalInt(oManifester, "PRC_PowerListType", POWER_LIST_MISC); + } + /* Get some data */ int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE; + // Manifester-related stuff - int nManifesterLevel = GetManifesterLevel(oManifester); + //int nManifesterLevel = GetManifesterLevel(oManifester); + int nManifesterLevel; + if(bIsHiddenTalent) + { + nManifesterLevel = GetManifesterLevel(oManifester, CLASS_TYPE_INVALID); + } + else + { + nManifesterLevel = GetManifesterLevel(oManifester); + } int nPowerLevel = GetPowerLevel(oManifester); int nClass = GetManifestingClass(oManifester); int nWildSurge = GetWildSurge(oManifester); @@ -714,6 +734,8 @@ struct manifestation EvaluateManifestation(object oManifester, object oTarget, s manif.nManifesterLevel = nManifesterLevel; manif.nSpellID = PRCGetSpellId(); + + // Run an ability score check to see if the manifester can manifest the power at all if (bIsPsiLike) { @@ -767,7 +789,9 @@ struct manifestation EvaluateManifestation(object oManifester, object oTarget, s //If the manifester does not have enough points before hostile modifiers, cancel power if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints) { - FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power" + // DEBUG: show why the cost over cap branch triggered + FloatingTextStringOnCreature("DEBUG: manif.nManifesterLevel=" + IntToString(manif.nManifesterLevel) + " manif.nPPCost=" + IntToString(manif.nPPCost) +" PRC_UsePowerList=" + IntToString(GetLocalInt(manif.oManifester, "PRC_UsePowerList")), manif.oManifester, FALSE); + FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power" manif.bCanManifest = FALSE; } // The manifester has enough power points that they would be able to use the power, barring extra costs diff --git a/src/include/psi_inc_pwresist.nss b/src/include/psi_inc_pwresist.nss index e1c5a96..447c917 100644 --- a/src/include/psi_inc_pwresist.nss +++ b/src/include/psi_inc_pwresist.nss @@ -14,6 +14,7 @@ #include "prc_class_const" */ #include "prc_alterations" +#include "prcsp_engine" // Constants that dictate ResistPower results const int POWER_RESIST_FAIL = 1; diff --git a/src/include/psi_spellhook.nss b/src/include/psi_spellhook.nss index 12c651d..aef699f 100644 --- a/src/include/psi_spellhook.nss +++ b/src/include/psi_spellhook.nss @@ -75,6 +75,15 @@ int PsiPrePowerCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode", oManifester); + //--------------------------------------------------------------------------- + // Forsakers can't use psionics + //--------------------------------------------------------------------------- + if (nContinue && GetLevelByClass(CLASS_TYPE_FORSAKER, oManifester)) + { + FloatingTextStringOnCreature("Forsakers cannot manifest psionic powers!", oManifester, FALSE); + nContinue = FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- @@ -209,3 +218,4 @@ int PsiPrePowerCastCode() return nContinue; } +//:: void main (){} \ No newline at end of file diff --git a/src/include/shd_inc_mystknwn.nss b/src/include/shd_inc_mystknwn.nss index b335485..0189d05 100644 --- a/src/include/shd_inc_mystknwn.nss +++ b/src/include/shd_inc_mystknwn.nss @@ -192,7 +192,8 @@ int PathFeatToIPFeat(int nFeat); /* Includes */ ////////////////////////////////////////////////// - +#include "inc_lookups" +#include "inc_pers_array" ////////////////////////////////////////////////// /* Internal functions */ @@ -519,7 +520,7 @@ int GetMaxMysteryLevelLearnable(object oShadow, int nClass, int nType) if(DEBUG) DoDebug("GetMaxMysteryLevelLearnable nType: " + IntToString(nType)); // Rules Quote: - // Within a category—Apprentice, Initiate, Master—you must have at least two mysteries of any given level + // Within a category�Apprentice, Initiate, Master�you must have at least two mysteries of any given level // before you can take any mysteries of the next higher level. For instance, you must have two 1st-level // mysteries before you can take any 2nds, and at least two 2nds before you can take any 3rds. int nMaxLrn, i, nMystLevel, nCount1, nCount2; diff --git a/src/include/shd_inc_shdfunc.nss b/src/include/shd_inc_shdfunc.nss index 6ae4f63..2d19e9f 100644 --- a/src/include/shd_inc_shdfunc.nss +++ b/src/include/shd_inc_shdfunc.nss @@ -210,6 +210,7 @@ int GetHasNocturnal(object oShadow, int nPath); #include "prc_alterations" #include "shd_inc_myst" #include "shd_inc_mystknwn" +#include "lookup_2da_spell" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/shd_mysthook.nss b/src/include/shd_mysthook.nss index 51c63be..dc9ad89 100644 --- a/src/include/shd_mysthook.nss +++ b/src/include/shd_mysthook.nss @@ -17,6 +17,8 @@ #include "prc_inc_spells" #include "inc_utility" #include "prc_inc_itmrstr" +#include "shd_inc_shdfunc" +#include "lookup_2da_spell" // This function holds all functions that are supposed to run before the actual // spellscript gets run. If this functions returns FALSE, the spell is aborted @@ -132,6 +134,15 @@ int ShadPreMystCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oShadow); + //--------------------------------------------------------------------------- + // Block forsakers from using shadowcasting + //--------------------------------------------------------------------------- + if(GetLevelByClass(CLASS_TYPE_FORSAKER, oShadow) > 0) + { + SendMessageToPC(oShadow, "Forsakers cannot use the power of shadowcasting."); + return FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- @@ -277,4 +288,6 @@ int ShadPreMystCastCode() if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #6: " + IntToString(nContinue)); return nContinue; -} \ No newline at end of file +} + +//:: void main (){} diff --git a/src/include/tob_movehook.nss b/src/include/tob_movehook.nss index 3ac1493..614e2a8 100644 --- a/src/include/tob_movehook.nss +++ b/src/include/tob_movehook.nss @@ -14,6 +14,7 @@ #include "prc_inc_spells" #include "inc_utility" #include "x2_inc_spellhook" +#include "tob_inc_tobfunc" // This function holds all functions that are supposed to run before the actual // spellscript gets run. If this functions returns FALSE, the spell is aborted @@ -78,7 +79,14 @@ int PreManeuverCastCode() //--------------------------------------------------------------------------- if(nContinue) nContinue = !GetLocalInt(oInitiator, "CrusaderBreak"); - + //--------------------------------------------------------------------------- + // Forsakers can't use supernatural maneuvers + //--------------------------------------------------------------------------- + if (nContinue && GetIsManeuverSupernatural(nMoveId) && GetLevelByClass(CLASS_TYPE_FORSAKER, oInitiator)) + { + FloatingTextStringOnCreature("Forsakers cannot use supernatural maneuvers!", oInitiator, FALSE); + nContinue = FALSE; + } //--------------------------------------------------------------------------- // Run NullPsionicsField Check //--------------------------------------------------------------------------- diff --git a/src/include/true_inc_truespk.nss b/src/include/true_inc_truespk.nss index 7523024..4787683 100644 --- a/src/include/true_inc_truespk.nss +++ b/src/include/true_inc_truespk.nss @@ -109,11 +109,14 @@ int GetIsSyllable(int nSpellId); */ int DoSpellTruenameCheck(object oTrueSpeaker, object oTarget, int nPersonal = FALSE); +string GetNormalUtterSpellId(int nSpellId); + ////////////////////////////////////////////////// /* Includes */ ////////////////////////////////////////////////// #include "prc_inc_spells" +#include "true_inc_trufunc" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/true_inc_trufunc.nss b/src/include/true_inc_trufunc.nss index 355783a..ad010b3 100644 --- a/src/include/true_inc_trufunc.nss +++ b/src/include/true_inc_trufunc.nss @@ -260,6 +260,7 @@ int GetCadenceCount(object oTrueSpeaker); #include "prc_alterations" #include "true_inc_utter" #include "true_inc_truknwn" +#include "true_inc_truespk" ////////////////////////////////////////////////// /* Function definitions */ diff --git a/src/include/true_inc_truknwn.nss b/src/include/true_inc_truknwn.nss index 22fbea0..9f9a606 100644 --- a/src/include/true_inc_truknwn.nss +++ b/src/include/true_inc_truknwn.nss @@ -141,6 +141,7 @@ int GetHasUtterance(int nUtter, object oCreature = OBJECT_SELF); #include "inc_pers_array" #include "prc_inc_nwscript" #include "inc_lookups" +#include "prc_x2_itemprop" ////////////////////////////////////////////////// /* Internal functions */ diff --git a/src/include/true_utterhook.nss b/src/include/true_utterhook.nss index 36b6b8c..dfb3671 100644 --- a/src/include/true_utterhook.nss +++ b/src/include/true_utterhook.nss @@ -18,6 +18,7 @@ #include "prc_inc_spells" #include "inc_utility" #include "prc_inc_itmrstr" +#include "true_inc_trufunc" // This function holds all functions that are supposed to run before the actual @@ -42,6 +43,16 @@ int TruePreUtterCastCode() int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oTrueSpeaker); + //--------------------------------------------------------------------------- + // Block forsakers from using truenaming + //--------------------------------------------------------------------------- + + if(GetLevelByClass(CLASS_TYPE_FORSAKER, oTrueSpeaker) > 0) + { + SendMessageToPC(oTrueSpeaker, "Forsakers cannot use the power of truenaming."); + return FALSE; + } + //--------------------------------------------------------------------------- // Break any spell require maintaining concentration //--------------------------------------------------------------------------- diff --git a/src/include/x2_inc_spellhook.nss b/src/include/x2_inc_spellhook.nss index 92668bf..5449590 100644 --- a/src/include/x2_inc_spellhook.nss +++ b/src/include/x2_inc_spellhook.nss @@ -2902,7 +2902,7 @@ int WandEquipped(object oCaster, object oSpellCastItem) int nType = GetBaseItemType(oSpellCastItem); - if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND) // Has to be a wand, obv + if(nType == BASE_ITEM_MAGICWAND || nType == BASE_ITEM_ENCHANTED_WAND || nType == BASE_ITEM_CRAFTED_SCEPTER) // Has to be a wand, obv { if(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCaster) == oSpellCastItem || GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCaster) == oSpellCastItem) // Needs to be equipped { @@ -2910,7 +2910,7 @@ int WandEquipped(object oCaster, object oSpellCastItem) } else { - FloatingTextStringOnCreature("You must equip a wand to cast from it.", oCaster, FALSE); + FloatingTextStringOnCreature("You must equip this item to cast from it.", oCaster, FALSE); return FALSE; // It's a wand not equipped } } diff --git a/src/include/x3_inc_horse.nss b/src/include/x3_inc_horse.nss index 65548a0..422e5c2 100644 --- a/src/include/x3_inc_horse.nss +++ b/src/include/x3_inc_horse.nss @@ -24,6 +24,7 @@ #include "x0_i0_position" #include "X0_INC_HENAI" #include "x3_inc_skin" +#include "prc_racial_const" /* @@ -638,7 +639,7 @@ int HorseGetMountTail(object oHorse); // FILE: x3_inc_horse FUNCTION: HorseGetMountFailureMessage() // This is a companion function to HorseGetCanBeMounted. If you need a text // message that explains why the horse cannot be mounted. -string HorseGetMountFailureMessage(object oTarget,object oRider=OBJECT_INVALID); +string HorseGetMountFailureMessage(object oHorse,object oRider=OBJECT_INVALID); // FILE: x3_inc_horse FUNCTION: HorseAddHorseMenu() @@ -1050,6 +1051,8 @@ void HORSE_SupportOriginalSpeed(object oRider) } // check to see if matches conditions eSearch=GetNextEffect(oRider); } // cycle through effects + + } // HORSE_SupportOriginalSpeed() diff --git a/src/module/dlg/oc_ai_henchmen.dlg.json b/src/module/dlg/oc_ai_henchmen.dlg.json index d9ca1b6..72c80ab 100644 --- a/src/module/dlg/oc_ai_henchmen.dlg.json +++ b/src/module/dlg/oc_ai_henchmen.dlg.json @@ -56,7 +56,50 @@ }, "ConditionParams": { "type": "list", - "value": [] + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "nTRUE" + }, + "Value": { + "type": "cexostring", + "value": "0" + } + } + ] + }, + "Index": { + "type": "dword", + "value": 248 + }, + "IsChild": { + "type": "byte", + "value": 0 + } + }, + { + "__struct_id": 1, + "Active": { + "type": "resref", + "value": "0c_if_scout" + }, + "ConditionParams": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "nTRUE" + }, + "Value": { + "type": "cexostring", + "value": "1" + } + } + ] }, "Index": { "type": "dword", @@ -68,7 +111,7 @@ } }, { - "__struct_id": 1, + "__struct_id": 2, "Active": { "type": "resref", "value": "0c_if_identify" @@ -87,7 +130,7 @@ } }, { - "__struct_id": 2, + "__struct_id": 3, "Active": { "type": "resref", "value": "0c_if_open_inven" @@ -106,7 +149,7 @@ } }, { - "__struct_id": 3, + "__struct_id": 4, "Active": { "type": "resref", "value": "" @@ -125,7 +168,7 @@ } }, { - "__struct_id": 4, + "__struct_id": 5, "Active": { "type": "resref", "value": "0c_if_has_class" @@ -199,103 +242,6 @@ "value": 0 } }, - { - "__struct_id": 5, - "Active": { - "type": "resref", - "value": "0c_if_has_class" - }, - "ConditionParams": { - "type": "list", - "value": [ - { - "__struct_id": 0, - "Key": { - "type": "cexostring", - "value": "nClass1" - }, - "Value": { - "type": "cexostring", - "value": "1" - } - }, - { - "__struct_id": 1, - "Key": { - "type": "cexostring", - "value": "nClass2" - }, - "Value": { - "type": "cexostring", - "value": "2" - } - }, - { - "__struct_id": 2, - "Key": { - "type": "cexostring", - "value": "nClass3" - }, - "Value": { - "type": "cexostring", - "value": "3" - } - }, - { - "__struct_id": 3, - "Key": { - "type": "cexostring", - "value": "nClass4" - }, - "Value": { - "type": "cexostring", - "value": "9" - } - }, - { - "__struct_id": 4, - "Key": { - "type": "cexostring", - "value": "nClass5" - }, - "Value": { - "type": "cexostring", - "value": "10" - } - }, - { - "__struct_id": 5, - "Key": { - "type": "cexostring", - "value": "nClass6" - }, - "Value": { - "type": "cexostring", - "value": "47" - } - }, - { - "__struct_id": 6, - "Key": { - "type": "cexostring", - "value": "nClass7" - }, - "Value": { - "type": "cexostring", - "value": "48" - } - } - ] - }, - "Index": { - "type": "dword", - "value": 109 - }, - "IsChild": { - "type": "byte", - "value": 0 - } - }, { "__struct_id": 6, "Active": { @@ -386,7 +332,7 @@ }, "Index": { "type": "dword", - "value": 77 + "value": 109 }, "IsChild": { "type": "byte", @@ -399,6 +345,103 @@ "type": "resref", "value": "0c_if_has_class" }, + "ConditionParams": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "nClass1" + }, + "Value": { + "type": "cexostring", + "value": "1" + } + }, + { + "__struct_id": 1, + "Key": { + "type": "cexostring", + "value": "nClass2" + }, + "Value": { + "type": "cexostring", + "value": "2" + } + }, + { + "__struct_id": 2, + "Key": { + "type": "cexostring", + "value": "nClass3" + }, + "Value": { + "type": "cexostring", + "value": "3" + } + }, + { + "__struct_id": 3, + "Key": { + "type": "cexostring", + "value": "nClass4" + }, + "Value": { + "type": "cexostring", + "value": "9" + } + }, + { + "__struct_id": 4, + "Key": { + "type": "cexostring", + "value": "nClass5" + }, + "Value": { + "type": "cexostring", + "value": "10" + } + }, + { + "__struct_id": 5, + "Key": { + "type": "cexostring", + "value": "nClass6" + }, + "Value": { + "type": "cexostring", + "value": "47" + } + }, + { + "__struct_id": 6, + "Key": { + "type": "cexostring", + "value": "nClass7" + }, + "Value": { + "type": "cexostring", + "value": "48" + } + } + ] + }, + "Index": { + "type": "dword", + "value": 77 + }, + "IsChild": { + "type": "byte", + "value": 0 + } + }, + { + "__struct_id": 8, + "Active": { + "type": "resref", + "value": "0c_if_has_class" + }, "ConditionParams": { "type": "list", "value": [ @@ -502,7 +545,7 @@ } }, { - "__struct_id": 8, + "__struct_id": 9, "Active": { "type": "resref", "value": "" @@ -521,7 +564,7 @@ } }, { - "__struct_id": 9, + "__struct_id": 10, "Active": { "type": "resref", "value": "" @@ -540,7 +583,7 @@ } }, { - "__struct_id": 10, + "__struct_id": 11, "Active": { "type": "resref", "value": "0c_if_hen_leave" @@ -559,7 +602,7 @@ } }, { - "__struct_id": 11, + "__struct_id": 12, "Active": { "type": "resref", "value": "0c_if_convo" @@ -578,7 +621,7 @@ } }, { - "__struct_id": 12, + "__struct_id": 13, "Active": { "type": "resref", "value": "" @@ -6591,7 +6634,19 @@ "__struct_id": 20, "ActionParams": { "type": "list", - "value": [] + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "sAction" + }, + "Value": { + "type": "cexostring", + "value": "GetHenchTokens" + } + } + ] }, "Animation": { "type": "dword", @@ -6696,11 +6751,23 @@ "__struct_id": 4, "Active": { "type": "resref", - "value": "x2_d1_hashench1" + "value": "0c_if_hen_target" }, "ConditionParams": { "type": "list", - "value": [] + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "nTarget" + }, + "Value": { + "type": "cexostring", + "value": "3" + } + } + ] }, "Index": { "type": "dword", @@ -6715,11 +6782,23 @@ "__struct_id": 5, "Active": { "type": "resref", - "value": "x2_d1_hashench2" + "value": "0c_if_hen_target" }, "ConditionParams": { "type": "list", - "value": [] + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "nTarget" + }, + "Value": { + "type": "cexostring", + "value": "4" + } + } + ] }, "Index": { "type": "dword", @@ -6734,11 +6813,23 @@ "__struct_id": 6, "Active": { "type": "resref", - "value": "x2_d1_hashench3" + "value": "0c_if_hen_target" }, "ConditionParams": { "type": "list", - "value": [] + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "nTarget" + }, + "Value": { + "type": "cexostring", + "value": "5" + } + } + ] }, "Index": { "type": "dword", @@ -6753,11 +6844,23 @@ "__struct_id": 7, "Active": { "type": "resref", - "value": "x2_d1_hashench4" + "value": "0c_if_hen_target" }, "ConditionParams": { "type": "list", - "value": [] + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "nTarget" + }, + "Value": { + "type": "cexostring", + "value": "6" + } + } + ] }, "Index": { "type": "dword", @@ -6795,7 +6898,7 @@ }, "Script": { "type": "resref", - "value": "x2_d1_gentags" + "value": "0c_assoc_actions" }, "Sound": { "type": "resref", @@ -8530,6 +8633,55 @@ "type": "list", "value": [] }, + "Animation": { + "type": "dword", + "value": 0 + }, + "AnimLoop": { + "type": "byte", + "value": 1 + }, + "Comment": { + "type": "cexostring", + "value": "" + }, + "Delay": { + "type": "dword", + "value": 0 + }, + "Quest": { + "type": "cexostring", + "value": "" + }, + "RepliesList": { + "type": "list", + "value": [] + }, + "Script": { + "type": "resref", + "value": "" + }, + "Sound": { + "type": "resref", + "value": "" + }, + "Speaker": { + "type": "cexostring", + "value": "" + }, + "Text": { + "type": "cexolocstring", + "value": { + "0": "" + } + } + }, + { + "__struct_id": 29, + "ActionParams": { + "type": "list", + "value": [] + }, "Animation": { "type": "dword", "value": 29 @@ -8565,7 +8717,7 @@ }, "Index": { "type": "dword", - "value": 249 + "value": 250 }, "IsChild": { "type": "byte", @@ -8584,7 +8736,7 @@ }, "Index": { "type": "dword", - "value": 248 + "value": 249 }, "IsChild": { "type": "byte", @@ -8616,7 +8768,7 @@ }, "NumWords": { "type": "dword", - "value": 1148 + "value": 1152 }, "PreventZoomIn": { "type": "byte", @@ -27034,12 +27186,69 @@ "Text": { "type": "cexolocstring", "value": { - "0": "I would like you to scout the area." + "0": "Would you stop patroling ahead." } } }, { "__struct_id": 248, + "ActionParams": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "sAction" + }, + "Value": { + "type": "cexostring", + "value": "Scout" + } + } + ] + }, + "Animation": { + "type": "dword", + "value": 0 + }, + "AnimLoop": { + "type": "byte", + "value": 1 + }, + "Comment": { + "type": "cexostring", + "value": "" + }, + "Delay": { + "type": "dword", + "value": 4294967295 + }, + "EntriesList": { + "type": "list", + "value": [] + }, + "Quest": { + "type": "cexostring", + "value": "" + }, + "Script": { + "type": "resref", + "value": "0c_assoc_actions" + }, + "Sound": { + "type": "resref", + "value": "" + }, + "Text": { + "type": "cexolocstring", + "value": { + "0": "I would like you to patrol ahead." + } + } + }, + { + "__struct_id": 249, "ActionParams": { "type": "list", "value": [] @@ -27084,7 +27293,7 @@ } }, { - "__struct_id": 249, + "__struct_id": 250, "ActionParams": { "type": "list", "value": [] @@ -27141,7 +27350,46 @@ }, "ConditionParams": { "type": "list", - "value": [] + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "sInput" + }, + "Value": { + "type": "cexostring", + "value": "Can_Hire_Henchman" + } + } + ] + }, + "Index": { + "type": "dword", + "value": 29 + } + }, + { + "__struct_id": 1, + "Active": { + "type": "resref", + "value": "0c_if_not_master" + }, + "ConditionParams": { + "type": "list", + "value": [ + { + "__struct_id": 0, + "Key": { + "type": "cexostring", + "value": "sInput" + }, + "Value": { + "type": "cexostring", + "value": "Cannot_Hire_Henchman" + } + } + ] }, "Index": { "type": "dword", @@ -27149,7 +27397,7 @@ } }, { - "__struct_id": 1, + "__struct_id": 2, "Active": { "type": "resref", "value": "0c_if_ass_convo" diff --git a/src/module/ifo/module.ifo.json b/src/module/ifo/module.ifo.json index b7c56a4..d04af58 100644 --- a/src/module/ifo/module.ifo.json +++ b/src/module/ifo/module.ifo.json @@ -904,7 +904,7 @@ }, "Mod_OnPlrGuiEvt": { "type": "resref", - "value": "" + "value": "prc_onplayergui" }, "Mod_OnPlrLvlUp": { "type": "resref", diff --git a/src/module/nss/0c_assoc_actions.nss b/src/module/nss/0c_assoc_actions.nss index 74975d4..d5b1867 100644 --- a/src/module/nss/0c_assoc_actions.nss +++ b/src/module/nss/0c_assoc_actions.nss @@ -17,10 +17,21 @@ void main() // Scout ahead is done int 0e_ch_1_hb (heartbeat script). if(sAction == "Scout") { - ai_ClearCreatureActions(); - ai_HaveCreatureSpeak(oAssociate, 4, ":29:35:46:"); - ai_SetAIMode(oAssociate, AI_MODE_SCOUT_AHEAD, TRUE); - ai_ScoutAhead(oAssociate); + if(ai_GetAIMode(oAssociate, AI_MODE_SCOUT_AHEAD)) + { + ai_ClearCreatureActions(); + ai_HaveCreatureSpeak(oAssociate, 6, ":29:35:46:10"); + ai_SetAIMode(oAssociate, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SendMessages(GetName(oAssociate) + " has stopped patrolling ahead.", AI_COLOR_YELLOW, oPC); + } + else + { + ai_ClearCreatureActions(); + ai_HaveCreatureSpeak(oAssociate, 6, ":29:35:46:22:"); + ai_SetAIMode(oAssociate, AI_MODE_SCOUT_AHEAD, TRUE); + ai_SendMessages(GetName(oAssociate) + " is now patrolling ahead.", AI_COLOR_YELLOW, oPC); + ai_ScoutAhead(oAssociate); + } } else if(sAction == "BasicTactics") { @@ -168,5 +179,22 @@ void main() } return; } + else if(sAction == "GetHenchTokens") + { + int nCount, nCntr = 1; + object oHenchman = GetHenchman(oPC, nCntr); + while(oHenchman != OBJECT_INVALID && nCntr <= AI_MAX_HENCHMAN) + { + if(oHenchman == OBJECT_INVALID) break; + if(oHenchman != oAssociate) + { + SetCustomToken(77101 + nCount, GetName(oHenchman)); + nCount++; + } + oHenchman = GetHenchman(oPC, ++nCntr); + } + ai_SetupAllyTargets(oAssociate, oPC); + return; + } aiSaveAssociateModesToDb(oPC, oAssociate); } diff --git a/src/module/nss/0c_cast_polymorp.nss b/src/module/nss/0c_cast_polymorp.nss index b06f183..9d10420 100644 --- a/src/module/nss/0c_cast_polymorp.nss +++ b/src/module/nss/0c_cast_polymorp.nss @@ -10,9 +10,6 @@ void main() { object oHenchman = OBJECT_SELF; int nSpell = StringToInt (GetScriptParam ("nSpell")); - // Save the original form so we can check when we turn back (Add 1 so we don't save a 0!). - SetLocalInt (oHenchman, AI_NORMAL_FORM, GetAppearanceType (oHenchman) + 1); - SetLocalString (oHenchman, AI_COMBAT_SCRIPT, "ai_a_polymorphed"); ActionCastSpellAtObject (nSpell, oHenchman, 255, TRUE); } diff --git a/src/module/nss/0c_get_convo.nss b/src/module/nss/0c_get_convo.nss index 7cdfd40..4e86741 100644 --- a/src/module/nss/0c_get_convo.nss +++ b/src/module/nss/0c_get_convo.nss @@ -9,8 +9,10 @@ Allows use of ai_conversation for henchman in other modules. */////////////////////////////////////////////////////////////////////////////// #include "0i_actions" +#include "nw_inc_gff" void BeginOriginalHenchmanConversation(string sDialog, object oPC) { + if(AI_DEBUG) ai_Debug("0c_get_convo", "14", "sDialog: (" + sDialog + ")"); BeginConversation(sDialog, oPC); } void main() diff --git a/src/module/nss/0c_henchmenspell.nss b/src/module/nss/0c_henchmenspell.nss index 5e64cba..f77728d 100644 --- a/src/module/nss/0c_henchmenspell.nss +++ b/src/module/nss/0c_henchmenspell.nss @@ -32,6 +32,7 @@ void main() { ai_ClearCreatureActions(); object oTarget = GetLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nTarget)); + if(AI_DEBUG) ai_Debug("0c_henchmenspell", "36", " nTarget: " + IntToString(nTarget) + " oTarget: " + GetName(oTarget)); if(oTarget != OBJECT_INVALID && ai_CheckAndCastSpell(oCreature, nSpell, 0, 0.0f, oTarget, oPC)) { DeleteLocalInt(oCreature, "0_SPELL_TO_CAST"); diff --git a/src/module/nss/0c_if_ass_convo.nss b/src/module/nss/0c_if_ass_convo.nss index 190a0f7..12cb19b 100644 --- a/src/module/nss/0c_if_ass_convo.nss +++ b/src/module/nss/0c_if_ass_convo.nss @@ -68,7 +68,7 @@ int StartingConditional() { string sHealingIn = IntToString(GetLocalInt(oAssociate, AI_HEAL_IN_COMBAT_LIMIT)) + "%"; string sHealingOut = IntToString(GetLocalInt(oAssociate, AI_HEAL_OUT_OF_COMBAT_LIMIT)) + "%"; - SetCustomToken(AI_BASE_CUSTOM_TOKEN + 5, "I'm healing our allies if they go below " + + SetCustomToken(AI_BASE_CUSTOM_TOKEN + 4, "I'm healing our allies if they go below " + sHealingIn + " health in combat and " + sHealingOut + " out of combat."); } else if(sParam == "Spells") diff --git a/src/module/nss/0c_if_hen_target.nss b/src/module/nss/0c_if_hen_target.nss new file mode 100644 index 0000000..cab5510 --- /dev/null +++ b/src/module/nss/0c_if_hen_target.nss @@ -0,0 +1,16 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_assoc_mode + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see the ally targets have been set for + this number target. + nTarget (INT) : 0 = ALL, 1 PC, 2 Caster, 3-6 = oPC's Henchman, 7 = PC's Familiar + 8 = PC's Animal Companion, 9 = PC's Summon. + Param: + nTarget - The target to check and see if they are set. +*/////////////////////////////////////////////////////////////////////////////// +int StartingConditional() +{ + string sTarget = GetScriptParam("nTarget"); + return GetIsObjectValid(GetLocalObject(OBJECT_SELF, "AI_ALLY_TARGET_" + sTarget)); +} diff --git a/src/module/nss/0c_if_not_master.nss b/src/module/nss/0c_if_not_master.nss index 73cb84e..7052205 100644 --- a/src/module/nss/0c_if_not_master.nss +++ b/src/module/nss/0c_if_not_master.nss @@ -5,7 +5,11 @@ Text Appears When script that checks if the speaker is the master of this henchman. */////////////////////////////////////////////////////////////////////////////// +#include "0i_constants" int StartingConditional() { - return !GetIsObjectValid(GetMaster()); + string sInput = GetScriptParam("sInput"); + if(sInput == "Can_Hire_Henchman" && AI_ALLOW_TAKING_HENCHMAN) return !GetIsObjectValid(GetMaster()); + else if(sInput == "Cannot_Hire_Henchman") return !GetIsObjectValid(GetMaster()); + return FALSE; } diff --git a/src/module/nss/0c_if_scout.nss b/src/module/nss/0c_if_scout.nss index 9f7b0a3..d35ba43 100644 --- a/src/module/nss/0c_if_scout.nss +++ b/src/module/nss/0c_if_scout.nss @@ -3,9 +3,14 @@ Programmer: Philos //////////////////////////////////////////////////////////////////////////////// Text Appears When script that check if scouting is activated on this server. + + Script Param: nTRUE - + if set to 1 then it will pass TRUE if they are in scout mode. + if set to 0 then it will pass TRUE if they are NOT in scout mode. */////////////////////////////////////////////////////////////////////////////// #include "0i_associates" int StartingConditional() { - return AI_SCOUT_AHEAD_ON; + int nTRUE = StringToInt(GetScriptParam("nTRUE")); + return AI_SCOUT_AHEAD_ON && ai_GetAIMode(OBJECT_SELF, AI_MODE_SCOUT_AHEAD) == nTRUE; } diff --git a/src/module/nss/0e_animations.nss b/src/module/nss/0e_animations.nss new file mode 100644 index 0000000..0fbb1a6 --- /dev/null +++ b/src/module/nss/0e_animations.nss @@ -0,0 +1,26 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_animations + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster Ambient Animations and Walk Waypoint code. + This code uses the Bioware systems, but can be rewritten to use what ever you + want! + This is called in the nw_c2_default1 - monster heartbeat script. +*/////////////////////////////////////////////////////////////////////////////// +#include "x0_i0_anims" +#include "0i_actions" +void main() +{ + if(!IsInConversation (OBJECT_SELF)) + { + if(GetWalkCondition(NW_WALK_FLAG_CONSTANT)) WalkWayPoints(); + if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS)) PlayMobileAmbientAnimations_NonAvian(); + else if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN)) PlayMobileAmbientAnimations_Avian(); + else if(GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS)) PlayImmobileAmbientAnimations(); + else if(GetLocalInt(GetModule(), AI_RULE_WANDER) && GetStandardFactionReputation(STANDARD_FACTION_HOSTILE) > 89) + { + ai_AmbientAnimations(); + } + } + +} diff --git a/src/module/nss/0e_c2_7_ondeath.nss b/src/module/nss/0e_c2_7_ondeath.nss index 129e81e..7d9570e 100644 --- a/src/module/nss/0e_c2_7_ondeath.nss +++ b/src/module/nss/0e_c2_7_ondeath.nss @@ -11,17 +11,19 @@ void main() object oCreature = OBJECT_SELF; // Added code to allow for permanent associates in the battle! object oModule = GetModule(); + if(AI_DEBUG) ai_Debug("0e_c2_7_ondeath", "14", "AI_RULE_PERM_ASSOC: " + IntToString(GetLocalInt(oModule, AI_RULE_PERM_ASSOC))); if(GetLocalInt(oModule, AI_RULE_PERM_ASSOC)) { object oAssociate; int nIndex; - for(nIndex = 1; nIndex < 5; nIndex++) + for(nIndex = 2; nIndex < 6; nIndex++) { oAssociate = GetAssociate(nIndex, oCreature); if(oAssociate != OBJECT_INVALID) { - SetIsDestroyable(FALSE, FALSE, FALSE); + SetIsDestroyable(FALSE, FALSE, FALSE, oAssociate); DelayCommand(0.1, ChangeToStandardFaction(oAssociate, STANDARD_FACTION_HOSTILE)); + DelayCommand(3.0, SetIsDestroyable(TRUE, FALSE, FALSE, oAssociate)); } } } diff --git a/src/module/nss/0e_ch_7_ondeath.nss b/src/module/nss/0e_ch_7_ondeath.nss index 76d955d..8997e84 100644 --- a/src/module/nss/0e_ch_7_ondeath.nss +++ b/src/module/nss/0e_ch_7_ondeath.nss @@ -10,32 +10,36 @@ void main() { object oCreature = OBJECT_SELF; // Added code to allow for permanent associates in the battle! - if(AI_DEBUG) ai_Debug("0e_ch_7_ondeath", "13", GetName(oCreature) + " has died!" + - " AI_RULE_PERM_ASSOC: " + IntToString(GetLocalInt(GetModule(), AI_RULE_PERM_ASSOC))); + if(AI_DEBUG) ai_Debug("0e_ch_7_ondeath", "13", GetName(oCreature) + " has died!"); object oModule = GetModule(); if(GetLocalInt(oModule, AI_RULE_PERM_ASSOC)) { object oAssociate; int nIndex; - for(nIndex = 2; nIndex < 5; nIndex++) + for(nIndex = 2; nIndex < 6; nIndex++) { oAssociate = GetAssociate(nIndex, oCreature); if(oAssociate != OBJECT_INVALID) { - SetIsDestroyable(FALSE, FALSE, FALSE); - ChangeFaction(oAssociate, oCreature); + if(AI_DEBUG) ai_Debug("0e_ch_7_ondeath", "24", GetName(oAssociate) + " being set to permanent!"); + SetIsDestroyable(FALSE, FALSE, FALSE, oAssociate); + DelayCommand(0.1, ChangeToStandardFaction(oAssociate, STANDARD_FACTION_HOSTILE)); + DelayCommand(3.0, SetIsDestroyable(TRUE, FALSE, FALSE, oAssociate)); } } } // Remove the widget! object oPC = GetMaster(oCreature); if(oPC != OBJECT_INVALID) - { +{ + if(AI_DEBUG) ai_Debug("0e_ch_7_ondeath", "35", GetName(oPC) + " Removing associates widget!"); NuiDestroy(oPC, NuiFindWindow(oPC, ai_GetAssociateType(oPC, oCreature) + AI_WIDGET_NUI)); DelayCommand(0.5, ai_CheckXPPartyScale(oCreature)); DelayCommand(2.0, ai_ClearCreatureActions(TRUE)); } DelayCommand(2.0, ai_ClearCombatState(oCreature)); - ExecuteScript(GetLocalString(oCreature, "AI_ON_DEATH")); + ChangeToStandardFaction(oCreature, STANDARD_FACTION_DEFENDER); + if(AI_DEBUG) ai_Debug("0e_ch_7_ondeath", "42", "Execute second OnDeath script: " + GetLocalString(oCreature, "AI_ON_DEATH")); + ExecuteScript(GetLocalString(oCreature, "AI_ON_DEATH"), oCreature); } diff --git a/src/module/nss/0e_nui.nss b/src/module/nss/0e_nui.nss index 7d18c65..17717bd 100644 --- a/src/module/nss/0e_nui.nss +++ b/src/module/nss/0e_nui.nss @@ -8,6 +8,7 @@ #include "nw_inc_gff" #include "x0_i0_assoc" #include "0i_menus" +#include "0i_module" #include "0i_player_target" // Save a window ID to the database. void ai_SaveWindowLocation(object oPC, int nToken, string sAssociateType, string sWindowID); @@ -32,10 +33,6 @@ void ai_RulePercDistInc(object oPC, object oModule, int nIncrement, int nToken); // Adds a spell to a json AI restricted spell list then returns jRules. // bRestrict = TRUE will add to the list FALSE will remove it from the list. json ai_AddRestrictedSpell(json jRules, int nSpell, int bRestrict = TRUE); -// Turns on oAssociate AI, Setting all event scripts. -void ai_TurnOn(object oPC, object oAssociate, string sAssociateType); -// Turns off oAssociate AI, Setting all event scripts. -void ai_TurnOff(object oPC, object oAssociate, string sAssociateType); // Adds a henchman back into the players party. object ai_AddHenchman(object oPC, json jHenchman, location lLocation, int nFamiliar, int nCompanion); @@ -94,10 +91,20 @@ void main() json jData = NuiGetUserData(oPC, nToken); object oAssociate = StringToObject(JsonGetString(JsonArrayGet(jData, 0))); string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + if(ai_GetIsDungeonMaster(oPC)) + { + if(!NuiFindWindow(oPC, "dm" + AI_WIDGET_NUI)) + { + ai_SendMessages(GetName(oPC) + " is now a Dungeon Master! Loading Dungeon Master widget.", AI_COLOR_YELLOW, oPC); + ai_CheckDMStart(oPC); + } + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + return; + } if(!ai_GetIsCharacter(oAssociate) && !GetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE") && (oAssociate == OBJECT_INVALID || GetMaster(oAssociate) != oPC)) { - ai_SendMessages("This creature is no longer in your party!", AI_COLOR_RED, oPC); + ai_SendMessages(GetName(oAssociate) + " is no longer in your party!", AI_COLOR_RED, oPC); DelayCommand(0.0, NuiDestroy(oPC, nToken)); return; } @@ -196,57 +203,6 @@ void main() aiSaveAssociateModesToDb(oPC, oPC); } } - else if(sElem == "btn_toggle_assoc_widget") - { - int bWidgetOff = !ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc"); - string sAssocType; - ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc", bWidgetOff); - object oAssoc = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); - if(oAssoc != OBJECT_INVALID) - { - sAssocType = ai_GetAssociateType(oPC, oAssoc); - ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); - if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); - else ai_CreateWidgetNUI(oPC, oAssoc); - } - oAssoc = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); - if(oAssoc != OBJECT_INVALID) - { - sAssocType = ai_GetAssociateType(oPC, oAssoc); - ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); - if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); - else ai_CreateWidgetNUI(oPC, oAssoc); - } - oAssoc = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); - if(oAssoc != OBJECT_INVALID) - { - sAssocType = ai_GetAssociateType(oPC, oAssoc); - ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); - if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); - else ai_CreateWidgetNUI(oPC, oAssoc); - } - oAssoc = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); - if(oAssoc != OBJECT_INVALID) - { - sAssocType = ai_GetAssociateType(oPC, oAssoc); - ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); - if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); - else ai_CreateWidgetNUI(oPC, oAssoc); - } - int nIndex; - object oHenchman; - for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) - { - oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); - if(oHenchman != OBJECT_INVALID) - { - sAssocType = ai_GetAssociateType(oPC, oHenchman); - ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oHenchman, sAssocType, bWidgetOff); - if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); - else ai_CreateWidgetNUI(oPC, oHenchman); - } - } - } else if(sElem == "btn_effect_icon") { if(ai_GetMagicMode(oPC, AI_MAGIC_EFFECT_ICON_REPORT)) @@ -280,10 +236,10 @@ void main() { int nMaxHenchmen = StringToInt(sText); if(nMaxHenchmen < 1) nMaxHenchmen = 1; - if(nMaxHenchmen > 12) + if(nMaxHenchmen > AI_MAX_HENCHMAN) { - nMaxHenchmen = 12; - ai_SendMessages("The maximum henchmen for this mod is 12!", AI_COLOR_RED, oPC); + nMaxHenchmen = AI_MAX_HENCHMAN; + ai_SendMessages("The maximum henchmen for this mod is " + IntToString(AI_MAX_HENCHMAN) + "!", AI_COLOR_RED, oPC); } SetMaxHenchmen(nMaxHenchmen); SetLocalInt(oModule, AI_RULE_MAX_HENCHMAN, nMaxHenchmen); @@ -318,7 +274,7 @@ void main() { int nNumber = StringToInt(sText); if(nNumber < 0) nNumber = 0; - else if(nNumber > 100) nNumber = 100; + else if(nNumber > 500) nNumber = 500; SetLocalInt(oModule, AI_INCREASE_MONSTERS_HP, nNumber); jRules = JsonObjectSet(jRules, AI_INCREASE_MONSTERS_HP, JsonInt(nNumber)); } @@ -354,6 +310,12 @@ void main() { SetLocalInt(oModule, AI_RULE_BUFF_MONSTERS, bCheck); jRules = JsonObjectSet(jRules, AI_RULE_BUFF_MONSTERS, JsonInt(bCheck)); + NuiSetBind(oPC, nToken, "chbx_full_buff_event", JsonBool(bCheck)); + } + else if(sElem == "chbx_full_buff_check") + { + SetLocalInt(oModule, AI_RULE_FULL_BUFF_MONSTERS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_FULL_BUFF_MONSTERS, JsonInt(bCheck)); } else if(sElem == "chbx_buff_summons_check") { @@ -579,6 +541,60 @@ void main() else if(sElem == "btn_familiar_name") ai_SetCompanionName(oPC, oAssociate, nToken, ASSOCIATE_TYPE_FAMILIAR); else if(sElem == "btn_companion_name") ai_SetCompanionName(oPC, oAssociate, nToken, ASSOCIATE_TYPE_ANIMALCOMPANION); else if(GetStringLeft(sElem, 11) == "btn_plugin_") ai_Plugin_Execute(oPC, sElem); + else if(sElem == "btn_toggle_assoc_widget") + { + int bWidgetOff = !ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc"); + string sAssocType, sText; + if(bWidgetOff) sText = " Associate Widgets [Off]"; + else sText = " Associate Widgets [On]"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_toggle_assoc_widget_tooltip", sText); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc", bWidgetOff); + object oAssoc = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + oAssoc = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + oAssoc = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + oAssoc = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + int nIndex; + object oHenchman; + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oHenchman != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oHenchman); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oHenchman, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oHenchman); + } + } + } } else if(sEvent == "watch") { @@ -597,10 +613,14 @@ void main() jPlugin = JsonArraySet(jPlugin, 1, JsonBool(bCheck)); jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); - DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + } } else if(sElem == "chbx_buff_rest_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_BUFF_REST, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_toggle_assoc_widget_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_ASSOC_WIDGETS_OFF, oAssociate, sAssociateType, nToken, sElem); else if(sElem == "chbx_cmd_action_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_ACTION, oAssociate, sAssociateType, nToken, sElem); else if(sElem == "chbx_cmd_guard_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_GUARD, oAssociate, sAssociateType, nToken, sElem); else if(sElem == "chbx_cmd_hold_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_HOLD, oAssociate, sAssociateType, nToken, sElem); @@ -623,8 +643,11 @@ void main() else if(sElem == "chbx_companion_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_COMPANION, oAssociate, sAssociateType, nToken, sElem); else if(sElem == "cmb_familiar_selected") ai_SetCompanionType(oPC, oAssociate, nToken, ASSOCIATE_TYPE_FAMILIAR); else if(sElem == "cmb_companion_selected") ai_SetCompanionType(oPC, oAssociate, nToken, ASSOCIATE_TYPE_ANIMALCOMPANION); - DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI))); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } } else if(sEvent == "mousescroll") { @@ -635,6 +658,9 @@ void main() if(sElem == "btn_cmd_follow" && oPC != oAssociate) ai_FollowIncrement(oPC, oAssociate, 1.0, sAssociateType); else if(sElem == "btn_follow_target") ai_FollowIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_buff_long") ai_DelaySpellSpeed(oPC, oAssociate, 0.1, sAssociateType); + else if(sElem == "btn_buff_short") ai_DelaySpellSpeed(oPC, oAssociate, 0.1, sAssociateType); + else if(sElem == "btn_buff_all") ai_DelaySpellSpeed(oPC, oAssociate, 0.1, sAssociateType); } else if(nMouseScroll == -1.0) // Scroll down { @@ -642,6 +668,9 @@ void main() if(sElem == "btn_cmd_follow" && oPC != oAssociate) ai_FollowIncrement(oPC, oAssociate, -1.0, sAssociateType); else if(sElem == "btn_follow_target") ai_FollowIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_buff_long") ai_DelaySpellSpeed(oPC, oAssociate, -0.1, sAssociateType); + else if(sElem == "btn_buff_short") ai_DelaySpellSpeed(oPC, oAssociate, -0.1, sAssociateType); + else if(sElem == "btn_buff_all") ai_DelaySpellSpeed(oPC, oAssociate, -0.1, sAssociateType); } } return; @@ -722,8 +751,11 @@ void main() else if(sElem == "chbx_ignore_traps_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_IGNORE_TRAPS, oAssociate, sAssociateType, nToken, sElem); else if(sElem == "chbx_perc_range_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_PERC_RANGE, oAssociate, sAssociateType, nToken, sElem); else if(sElem == "cmb_ai_script_selected") ai_SetAIScript(oPC, oAssociate, nToken); - DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI))); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } } else if(sEvent == "mousescroll") { @@ -863,6 +895,60 @@ void main() else if(sElem == "btn_update_widget") ai_UpdateAssociateWidget(oPC, oAssociate); else if(GetStringLeft(sElem, 15) == "btn_exe_plugin_") ai_Plugin_Execute(oPC, sElem); else if(GetStringLeft(sElem, 11) == "btn_widget_") ai_SelectWidgetSpellTarget(oPC, oAssociate, sElem); + else if(sElem == "btn_toggle_assoc_widget") + { + int bWidgetOff = !ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc"); + string sAssocType, sText; + if(bWidgetOff) sText = "Associate Widgets [Off]"; + else sText = "Associate Widgets [On]"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_toggle_assoc_widget_tooltip", sText); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc", bWidgetOff); + object oAssoc = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + oAssoc = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + oAssoc = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + oAssoc = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oAssoc); + } + int nIndex; + object oHenchman; + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oHenchman != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oHenchman); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oHenchman, sAssocType, bWidgetOff); + if(bWidgetOff) IsWindowClosed(oPC, sAssocType + AI_WIDGET_NUI); + else ai_CreateWidgetNUI(oPC, oHenchman); + } + } + } } if(sEvent == "mousescroll") { @@ -881,6 +967,9 @@ void main() else if(sElem == "btn_heal_in") ai_Heal_Button(oPC, oAssociate, 5, AI_HEAL_IN_COMBAT_LIMIT, sAssociateType); else if(sElem == "btn_loot") ai_LootRangeIncrement(oPC, oAssociate, 1.0, sAssociateType); else if(sElem == "btn_perc_range") ai_PercRangeIncrement(oPC, oAssociate, 1, sAssociateType, -1); + else if(sElem == "btn_buff_long") ai_DelaySpellSpeed(oPC, oAssociate, 0.1, sAssociateType); + else if(sElem == "btn_buff_short") ai_DelaySpellSpeed(oPC, oAssociate, 0.1, sAssociateType); + else if(sElem == "btn_buff_all") ai_DelaySpellSpeed(oPC, oAssociate, 0.1, sAssociateType); } if(nMouseScroll == -1.0) // Scroll down { @@ -897,6 +986,9 @@ void main() else if(sElem == "btn_heal_in") ai_Heal_Button(oPC, oAssociate, -5, AI_HEAL_IN_COMBAT_LIMIT, sAssociateType); else if(sElem == "btn_loot") ai_LootRangeIncrement(oPC, oAssociate, -1.0, sAssociateType); else if(sElem == "btn_perc_range") ai_PercRangeIncrement(oPC, oAssociate, -1, sAssociateType, -1); + else if(sElem == "btn_buff_long") ai_DelaySpellSpeed(oPC, oAssociate, -0.1, sAssociateType); + else if(sElem == "btn_buff_short") ai_DelaySpellSpeed(oPC, oAssociate, -0.1, sAssociateType); + else if(sElem == "btn_buff_all") ai_DelaySpellSpeed(oPC, oAssociate, -0.1, sAssociateType); } } if(sEvent == "mousedown") @@ -1174,9 +1266,10 @@ void main() { json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_buffing"); - jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_forcerest"); - jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_henchmen"); jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_crafting"); + jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_forcerest"); + jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_fast_travel"); + jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_henchmen"); jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_mod_set"); jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_debug"); jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_test"); @@ -1209,8 +1302,11 @@ void main() ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); DelayCommand(0.0, NuiDestroy(oPC, nToken)); DelayCommand(0.1, ai_CreatePluginNUI(oPC)); - DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + } } if(sElem == "btn_clear_plugins") { @@ -1229,8 +1325,11 @@ void main() ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); DelayCommand(0.0, NuiDestroy(oPC, nToken)); DelayCommand(0.1, ai_CreatePluginNUI(oPC)); - DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + } } else if(sElem == "btn_add_plugin") { @@ -1249,8 +1348,11 @@ void main() ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); DelayCommand(0.0, NuiDestroy(oPC, nToken)); DelayCommand(0.1, ai_CreatePluginNUI(oPC)); - DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + } } else if(GetStringLeft(sElem, 11) == "btn_plugin_") ai_Plugin_Execute(oPC, sElem); } @@ -1265,8 +1367,11 @@ void main() jPlugin = JsonArraySet(jPlugin, 1, JsonBool(bCheck)); jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); - DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + } } } return; @@ -1311,7 +1416,8 @@ void main() jWidget = JsonArray(); if(JsonGetLength(jSpells) == 2) jSpells = JsonArrayInsert(jSpells, JsonArray()); } - if(JsonGetLength(jWidget) < 20) + int nWidgetLength = JsonGetLength(jWidget); + if(nWidgetLength < 20) { json jData = NuiGetUserData(oPC, nToken); json jQuickListArray = JsonArrayGet(jData, 1); @@ -1320,8 +1426,7 @@ void main() jSpells = JsonArraySet(jSpells, 2, jWidget); jAIData = JsonArraySet(jAIData, 10, jSpells); ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); - DelayCommand(0.0, NuiDestroy(oPC, nToken)); - DelayCommand(0.1, ai_CreateQuickWidgetSelectionNUI(oPC, oAssociate)); + ai_PopulateWidgetList(oPC, oAssociate, nToken, jWidget); } else ai_SendMessages("The quick widget can only have 20 abilities or spells!", AI_COLOR_RED, oPC); } @@ -1331,7 +1436,7 @@ void main() json jSpell = JsonArrayGet(jQuickListArray, nIndex); ai_CreateDescriptionNUI(oPC, jSpell); } - else if(GetStringLeft(sElem, 11) == "btn_widget_") + else if(GetStringLeft(sElem, 11) == "btn_widget_") // Removes ability from quick use widget { string sIndex; if(GetStringLength(sElem) == 13) sIndex = GetStringRight(sElem, 2); @@ -1343,17 +1448,19 @@ void main() jSpells = JsonArraySet(jSpells, 2, jWidget); jAIData = JsonArraySet(jAIData, 10, jSpells); ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); - DelayCommand(0.0, NuiDestroy(oPC, nToken)); - DelayCommand(0.1, ai_CreateQuickWidgetSelectionNUI(oPC, oAssociate)); + ai_PopulateWidgetList(oPC, oAssociate, nToken, jWidget); } } else if(sEvent == "close") { - int nUIToken = NuiFindWindow(oPC, sAssociateType + AI_QUICK_WIDGET_NUI); - if(nUIToken) + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) { - DelayCommand(0.0, NuiDestroy(oPC, nUIToken)); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + int nUIToken = NuiFindWindow(oPC, sAssociateType + AI_QUICK_WIDGET_NUI); + if(nUIToken) + { + DelayCommand(0.0, NuiDestroy(oPC, nUIToken)); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } } } return; @@ -1393,23 +1500,22 @@ void main() int nClass = GetClassByPosition(JsonGetInt(JsonArrayGet(jSpells, 0)), oAssociate); int nLevel = JsonGetInt(JsonArrayGet(jSpells, 1)); json jSpellArray = JsonArrayGet(jData, 1); + int nSpell = JsonGetInt(JsonArrayGet(jSpellArray, nIndex)); + string sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + string sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + string sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + int nSlot; int nMaxMemorizationSlot = GetMemorizedSpellCountByLevel(oAssociate, nClass, nLevel); - int nSlot, nSpell; + string sSlot; while(nSlot < nMaxMemorizationSlot) { if(GetMemorizedSpellId(oAssociate, nClass, nLevel, nSlot) == -1) { - nSpell = JsonGetInt(JsonArrayGet(jSpellArray, nIndex)); SetMemorizedSpell(oAssociate, nClass, nLevel, nSlot, nSpell, FALSE); - //NuiDestroy(oPC, nToken); - //ai_CreateSpellMemorizationNUI(oPC, oAssociate); - string sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); - string sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - string sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - string sIndex = IntToString(nSlot); - NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_image", JsonString(sSpellIcon)); - NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); + sSlot = IntToString(nSlot); + NuiSetBind(oPC, nToken, "btn_memorized_" + sSlot + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_memorized_" + sSlot + "_image", JsonString(sSpellIcon)); + NuiSetBind(oPC, nToken, "btn_memorized_" + sSlot + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); return; } nSlot++; @@ -1422,28 +1528,29 @@ void main() int nSpell = JsonGetInt(JsonArrayGet(jSpellArray, nIndex)); ai_CreateDescriptionNUI(oPC, JsonArray(), nSpell); } - else if(GetStringLeft(sElem, 14) == "btn_memorized_") + else if(GetStringLeft(sElem, 14) == "btn_memorized_") // Remove memorized spell. { json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); json jSpells = JsonArrayGet(jAIData, 10); int nClass = GetClassByPosition(JsonGetInt(JsonArrayGet(jSpells, 0)), oAssociate); int nLevel = JsonGetInt(JsonArrayGet(jSpells, 1)); - string sIndex = GetStringRight(sElem, 1); - ClearMemorizedSpell(oAssociate, nClass, nLevel, StringToInt(sIndex)); - NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); - NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_tooltip", JsonString("")); - NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_event", JsonBool(FALSE)); - //NuiDestroy(oPC, nToken); - //ai_CreateSpellMemorizationNUI(oPC, oAssociate); + string sSlot = GetStringRight(sElem, 1); + ClearMemorizedSpell(oAssociate, nClass, nLevel, StringToInt(sSlot)); + NuiSetBind(oPC, nToken, "btn_memorized_" + sSlot + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "btn_memorized_" + sSlot + "_tooltip", JsonString("")); + NuiSetBind(oPC, nToken, "btn_memorized_" + sSlot + "_event", JsonBool(FALSE)); } } else if(sEvent == "close") { - int nUIToken = NuiFindWindow(oPC, sAssociateType + AI_QUICK_WIDGET_NUI); - if(nUIToken) + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) { - DelayCommand(0.0, NuiDestroy(oPC, nUIToken)); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + int nUIToken = NuiFindWindow(oPC, sAssociateType + AI_QUICK_WIDGET_NUI); + if(nUIToken) + { + DelayCommand(0.0, NuiDestroy(oPC, nUIToken)); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } } } return; @@ -1500,6 +1607,7 @@ void main() jKnownList = JsonArray(); } int nMaxKnownSlots, nSlot; + string sClass, sName, sSpellIcon, sSlot; string sSpellKnownTable = Get2DAString("classes", "SpellKnownTable", nClass); if(sSpellKnownTable != "") nMaxKnownSlots = StringToInt(Get2DAString(sSpellKnownTable, "SpellLevel" + sLevel, GetLevelByClass(nClass, oAssociate) - 1)); else nMaxKnownSlots = 20; @@ -1511,13 +1619,13 @@ void main() jSpell = GffAddWord(JsonObject(), "Spell", nSpell); jSpell = JsonObjectSet(jSpell, "__struct_id", JsonInt(3)); jKnownList = JsonArrayInsert(jKnownList, jSpell); - string sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); - string sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - string sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - string sIndex = IntToString(nSlot); - NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_image", JsonString(sSpellIcon)); - NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + sLevel + ")")); + sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + sSlot = IntToString(nSlot); + NuiSetBind(oPC, nToken, "btn_known_" + sSlot + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_known_" + sSlot + "_image", JsonString(sSpellIcon)); + NuiSetBind(oPC, nToken, "btn_known_" + sSlot + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + sLevel + ")")); SetLocalInt(oAssociate, "AI_KNOWN_SPELL_CHANGE", TRUE); break; } @@ -1618,6 +1726,7 @@ void main() if(GetLocalInt(oAssociate, "AI_KNOWN_SPELL_CHANGE")) { RemoveHenchman(oPC, oAssociate); + ChangeToStandardFaction(oAssociate, STANDARD_FACTION_DEFENDER); json jHenchman = ObjectToJson(oAssociate, TRUE); json jClassList = GetLocalJson(oAssociate, AI_CLASS_LIST_JSON); jHenchman = GffReplaceList(jHenchman, "ClassList", jClassList); @@ -1630,8 +1739,12 @@ void main() AssignCommand(oAssociate, SetIsDestroyable(TRUE, FALSE, FALSE)); DestroyObject(oAssociate); oAssociate = ai_AddHenchman(oPC, jHenchman, lLocation, nFamiliar, nCompanion); + DeleteLocalJson(oAssociate, AI_CLASS_LIST_JSON); DeleteLocalInt(oAssociate, "AI_KNOWN_SPELL_CHANGE"); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } } } return; @@ -1701,7 +1814,11 @@ void ai_AddAssociate(object oPC, int nToken, json jAssociate, location lLocation AddHenchman(oPC, oAssociate); DeleteLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE"); DelayCommand(0.0, NuiDestroy(oPC, nToken)); - DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType) || oPC == oAssociate) + { + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } if(nRange) SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION, nRange); if(nFamiliar) SummonFamiliar(oAssociate); if(nCompanion) SummonAnimalCompanion(oAssociate); @@ -1819,12 +1936,12 @@ void ai_Perc_Range(object oPC, object oAssociate, int nToken, string sAssociateT SetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE", TRUE); int nBtnPercRange = GetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION + "_MENU"); string sText, sText2; - float fRange = 20.0; + float fRange = 25.0; if(nBtnPercRange == 8) { sText = "short"; sText2 = " Perception Range Short [10 meters Sight / 10 meters Listen]"; - fRange = 10.0; + fRange = 15.0; } else if(nBtnPercRange == 9) { @@ -1835,7 +1952,7 @@ void ai_Perc_Range(object oPC, object oAssociate, int nToken, string sAssociateT { sText = "long"; sText2 = " Perception Range Long [35 meters Sight / 20 meters Listen]"; - fRange = 35.0; + fRange = 40.0; } else if(nBtnPercRange == 11) { @@ -1917,49 +2034,6 @@ json ai_AddRestrictedSpell(json jRules, int nSpell, int bRestrict = TRUE) SetLocalJson(oModule, AI_RULE_RESTRICTED_SPELLS, jRSpells); return JsonObjectSet(jRules, AI_RULE_RESTRICTED_SPELLS, jRSpells); } -void ai_TurnOn(object oPC, object oTarget, string sAssociateType) -{ - ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ai_tooltip", " AI On"); - ai_SendMessages("AI turned on for " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "xx_pc_1_hb"); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_NOTICE, "xx_pc_2_percept"); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "xx_pc_3_endround"); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "xx_pc_4_convers"); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "xx_pc_5_phyatked"); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "xx_pc_6_damaged"); - //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DEATH, ""); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "xx_pc_8_disturb"); - //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); - //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_RESTED, ""); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "xx_pc_b_castat"); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "xx_pc_e_blocked"); - //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); - // This sets the script for the PC to run AI based on class. - ai_SetAssociateAIScript(oTarget, FALSE); - // Set so PC can hear associates talking in combat. - ai_SetListeningPatterns(oTarget); -} -void ai_TurnOff(object oPC, object oAssociate, string sAssociateType) -{ - ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ai_tooltip", " AI Off"); - ai_SendMessages("AI Turned off for " + GetName(oAssociate) + ".", AI_COLOR_YELLOW, oPC); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_NOTICE, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DAMAGED, ""); - //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DEATH, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DISTURBED, ""); - //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); - //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_RESTED, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, ""); - SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, ""); - //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); - DeleteLocalInt(oAssociate, "AI_I_AM_BEING_HEALED"); - DeleteLocalString(oAssociate, "AIScript"); - ai_ClearCreatureActions(); -} object ai_AddHenchman(object oPC, json jHenchman, location lLocation, int nFamiliar, int nCompanion) { jHenchman = GffReplaceResRef(jHenchman, "ScriptSpawn", ""); diff --git a/src/module/nss/0e_nui_dm.nss b/src/module/nss/0e_nui_dm.nss index 4ffd851..4a1dea0 100644 --- a/src/module/nss/0e_nui_dm.nss +++ b/src/module/nss/0e_nui_dm.nss @@ -5,6 +5,7 @@ Menu event script sEvent: close, click, mousedown, mouseup, watch (if bindwatch is set). /*////////////////////////////////////////////////////////////////////////////// +#include "0i_menus" #include "0i_menus_dm" void ai_SetDMWidgetButtonToCheckbox(object oDM, int nButton, int nToken, string sElem); void ai_SetDMWAccessButtonToCheckbox(object oDM, int nButton, int nToken, string sElem); @@ -32,6 +33,16 @@ void main() // " nToken: " + IntToString(nToken) + " oPC: " + GetName(oPC)); //WriteTimestampedLogEntry("0e_nui, 58, sWndId: " + sWndId + " sEvent: " + sEvent + " sElem: " + sElem + // " nToken: " + IntToString(nToken) + " oDM: " + GetName(oDM)); + if(ai_GetIsCharacter(oDM)) + { + if(!NuiFindWindow(oDM, "pc" + AI_WIDGET_NUI)) + { + ai_SendMessages(GetName(oDM) + " is now a Player! Loading player widget.", AI_COLOR_YELLOW, oDM); + ai_CreateWidgetNUI(oDM, oDM); + } + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + return; + } //************************************************************************** string sName = ai_RemoveIllegalCharacters(GetName(oDM)); // Watch to see if the window moves and save. @@ -197,10 +208,10 @@ void main() { int nMaxHenchmen = StringToInt(JsonGetString(NuiGetBind(oDM, nToken, sElem))); if(nMaxHenchmen < 1) nMaxHenchmen = 1; - if(nMaxHenchmen > 12) + if(nMaxHenchmen > AI_MAX_HENCHMAN) { - nMaxHenchmen = 12; - ai_SendMessages("The maximum henchmen for this mod is 12!", AI_COLOR_RED, oDM); + nMaxHenchmen = AI_MAX_HENCHMAN; + ai_SendMessages("The maximum henchmen for this mod is " + IntToString(AI_MAX_HENCHMAN) + "!", AI_COLOR_RED, oDM); } SetMaxHenchmen(nMaxHenchmen); json jRules = ai_GetCampaignDbJson("rules"); @@ -232,7 +243,7 @@ void main() { int nNumber = StringToInt(JsonGetString(NuiGetBind(oDM, nToken, sElem))); if(nNumber < 0) nNumber = 0; - else if(nNumber > 100) nNumber = 100; + else if(nNumber > 500) nNumber = 500; SetLocalInt(GetModule(), AI_INCREASE_MONSTERS_HP, nNumber); json jRules = ai_GetCampaignDbJson("rules"); jRules = JsonObjectSet(jRules, AI_INCREASE_MONSTERS_HP, JsonInt(nNumber)); @@ -253,6 +264,11 @@ void main() SetLocalInt(oModule, AI_RULE_BUFF_MONSTERS, bCheck); jRules = JsonObjectSet(jRules, AI_RULE_BUFF_MONSTERS, JsonInt(bCheck)); } + else if(sElem == "chbx_full_buff_check") + { + SetLocalInt(oModule, AI_RULE_FULL_BUFF_MONSTERS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_FULL_BUFF_MONSTERS, JsonInt(bCheck)); + } else if(sElem == "chbx_buff_summons_check") { SetLocalInt(oModule, AI_RULE_PRESUMMON, bCheck); diff --git a/src/module/nss/0e_onclientload.nss b/src/module/nss/0e_onclientload.nss index 041d49f..5f211f4 100644 --- a/src/module/nss/0e_onclientload.nss +++ b/src/module/nss/0e_onclientload.nss @@ -15,9 +15,5 @@ void main() object oCreature = OBJECT_SELF; // This can be moved to the OnClientLoad script event of your module. if(ai_GetIsCharacter(oCreature)) ai_CheckPCStart(oCreature); - // If this is a server you can add this as well. - else if(AI_SERVER && (GetIsDM(oCreature) || GetIsPlayerDM(oCreature))) - { - ai_CheckPCStart(oCreature); - } + else if(ai_GetIsDungeonMaster(oCreature)) ai_CheckDMStart(oCreature); } diff --git a/src/module/nss/0e_player_target.nss b/src/module/nss/0e_player_target.nss index 16b83fb..0e3b5be 100644 --- a/src/module/nss/0e_player_target.nss +++ b/src/module/nss/0e_player_target.nss @@ -10,6 +10,8 @@ action of the target. AI_TARGET_MODE is the constant used. AI_TARGET_ASSOCIATE is the associate that triggered the target mode. + + AI_TARGET_MODE_ON defines if the player is in target mode for a henchman instead of the PC. /*////////////////////////////////////////////////////////////////////////////// #include "0i_player_target" void main() @@ -31,6 +33,7 @@ void main() location lLocation = Location(GetArea(oPC), vTarget, GetFacing(oPC)); object oAssociate = GetLocalObject(oPC, AI_TARGET_ASSOCIATE); string sTargetMode = GetLocalString(oPC, AI_TARGET_MODE); + DeleteLocalString(oPC, AI_TARGET_MODE); // ********************* Exiting Target Actions ************************ // If the user manually exited targeting mode without selecting a target, return if(!GetIsObjectValid(oTarget) && vTarget == Vector()) @@ -38,50 +41,61 @@ void main() if(sTargetMode == "ASSOCIATE_ACTION_ALL") { ai_SendMessages("You have exited selecting an action for the party.", AI_COLOR_YELLOW, oPC); - if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") - { - if(GetLocalInt(oPC, sGhostModeVarname)) ai_OriginalRemoveAllActionMode(oPC); - } - else ai_RemoveAllActionMode(oPC); + ai_RemoveAllActionMode(oPC); + return; } else if(sTargetMode == "ASSOCIATE_ACTION") { ai_SendMessages("You have exited selecting an action for " + GetName(oAssociate) + ".", AI_COLOR_YELLOW, oPC); - if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + // Clean up any PC AI being turned on as well as variables. + DeleteLocalObject(oPC, AI_TARGET_ASSOCIATE); + DeleteLocalInt(oPC, AI_TARGET_MODE_ON); + DeleteLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE); + // Make sure the camera goes back to the player since we are leaving henchmen control. + if(GetLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE") != OBJECT_INVALID) { - if(GetLocalInt(oPC, sGhostModeVarname)) - { - ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); - DeleteLocalInt(oAssociate, sGhostModeVarname); - } + DeleteLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE"); + AttachCamera(oPC, oPC); + if(!GetLocalInt(GetModule(), AI_USING_PRC)) ai_TurnOff(oPC, oPC, "pc"); } - else + ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, FALSE); + if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) && !ai_GetAIMode(oPC, AI_MODE_GHOST) && + GetLocalInt(oAssociate, sGhostModeVarname)) { - ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, FALSE); - if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) && !ai_GetAIMode(oPC, AI_MODE_GHOST) && - GetLocalInt(oAssociate, sGhostModeVarname)) - { - - ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); - DeleteLocalInt(oAssociate, sGhostModeVarname); - } - ExecuteScript("nw_ch_ac1", oAssociate); + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); } + ExecuteScript("nw_ch_ac1", oAssociate); + return; } else if(sTargetMode == "ASSOCIATE_GET_TRAP") { ai_SendMessages(GetName(oAssociate) + " has exited selecing a trap!", AI_COLOR_YELLOW, oPC); + if(GetLocalInt(oPC, AI_TARGET_MODE_ON)) ai_EnterAssociateTargetMode(oPC, GetLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE)); + return; } else if(sTargetMode == "ASSOCIATE_PLACE_TRAP") { ai_SendMessages(GetName(oAssociate) + " has exited placing the trap!", AI_COLOR_YELLOW, oPC); + if(GetLocalInt(oPC, AI_TARGET_MODE_ON)) ai_EnterAssociateTargetMode(oPC, GetLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE)); + return; } else if(sTargetMode == "DM_SELECT_CAMERA_VIEW") { AttachCamera(oPC, oPC); ai_SendMessages(GetName(oPC) + " has defaulted camera view back to the player!", AI_COLOR_YELLOW, oPC); + return; + } + // If these actions are canceled and we are in target mode with a henchmen + // then turn target mode back on for that henchmen. + else if(sTargetMode == "ASSOCIATE_USE_ITEM" || + sTargetMode == "ASSOCIATE_USE_FEAT" || + sTargetMode == "ASSOCIATE_CAST_SPELL" || + sTargetMode == "ASSOCIATE_FOLLOW_TARGET") + { + if(GetLocalInt(oPC, AI_TARGET_MODE_ON)) ai_EnterAssociateTargetMode(oPC, GetLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE)); + return; } - return; } // ************************* Targeted Actions ************************** else @@ -89,45 +103,60 @@ void main() // This action makes an associates move to vTarget. if(sTargetMode == "ASSOCIATE_ACTION_ALL") { - if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") - { - ai_OriginalActionAllAssociates(oPC, oTarget, lLocation); - } - else ai_ActionAllAssociates(oPC, oTarget, lLocation); + ai_ActionAllAssociates(oPC, oTarget, lLocation); + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION_ALL"); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + return; } else if(sTargetMode == "ASSOCIATE_ACTION") { - if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") - { - AssignCommand(oAssociate, ai_OriginalActionAssociate(oPC, oTarget, lLocation)); - } - else AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation)); + AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation)); + return; + } + else if(sTargetMode == "ASSOCIATE_FOLLOW_TARGET") + { + ai_SelectFollowTarget(oPC, oAssociate, oTarget); + return; + } + else if(sTargetMode == "ASSOCIATE_GET_TRAP") + { + ai_SelectTrap(oPC, oAssociate, oTarget); + return; + } + else if(sTargetMode == "ASSOCIATE_PLACE_TRAP") + { + AssignCommand(oAssociate, ai_PlaceTrap(oPC, lLocation)); + return; } - else if(sTargetMode == "ASSOCIATE_FOLLOW_TARGET") ai_SelectFollowTarget(oPC, oAssociate, oTarget); - else if(sTargetMode == "ASSOCIATE_GET_TRAP") ai_SelectTrap(oPC, oAssociate, oTarget); - else if(sTargetMode == "ASSOCIATE_PLACE_TRAP") AssignCommand(oAssociate, ai_PlaceTrap(oPC, lLocation)); else if(sTargetMode == "ASSOCIATE_USE_ITEM") { if(oTarget == GetArea(oPC)) oTarget = OBJECT_INVALID; ai_UseWidgetItem(oPC, oAssociate, oTarget, lLocation); DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + if(GetLocalInt(oPC, AI_TARGET_MODE_ON)) ai_EnterAssociateTargetMode(oPC, GetLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE)); + return; } else if(sTargetMode == "ASSOCIATE_USE_FEAT") { if(oTarget == GetArea(oPC)) oTarget = OBJECT_INVALID; ai_UseWidgetFeat(oPC, oAssociate, oTarget, lLocation); DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + if(GetLocalInt(oPC, AI_TARGET_MODE_ON)) ai_EnterAssociateTargetMode(oPC, GetLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE)); + return; } else if(sTargetMode == "ASSOCIATE_CAST_SPELL") { if(oTarget == GetArea(oPC)) oTarget = OBJECT_INVALID; ai_CastWidgetSpell(oPC, oAssociate, oTarget, lLocation); DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + if(GetLocalInt(oPC, AI_TARGET_MODE_ON)) ai_EnterAssociateTargetMode(oPC, GetLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE)); + return; } else if(sTargetMode == "DM_SELECT_CAMERA_VIEW") { AttachCamera(oPC, oTarget); ai_SendMessages(GetName(oPC) + " has changed the camera view to " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); + return; } else if(sTargetMode == "DM_SELECT_OPEN_INVENTORY") { @@ -137,18 +166,20 @@ void main() ai_SendMessages("You have opened the inventory of "+ GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); } else ai_SendMessages(GetName(oTarget) + " is not in your line of sight!", AI_COLOR_YELLOW, oPC); + return; } else if(GetStringLeft(sTargetMode, 15) == "DM_SELECT_GROUP") { ai_AddToGroup(oPC, oTarget, sTargetMode); + return; } else if(GetStringLeft(sTargetMode, 15) == "DM_ACTION_GROUP") { ai_DMAction(oPC, oTarget, lLocation, sTargetMode); + return; } - // Get saved module player target script and execute it for pass through compatibility. - string sModuleTargetScript = GetLocalString(GetModule(), AI_MODULE_TARGET_EVENT); - ExecuteScript(sModuleTargetScript); } + // Get saved module player target script and execute it for pass through compatibility. + ExecuteScript(GetLocalString(GetModule(), AI_MODULE_TARGET_EVENT)); } } diff --git a/src/module/nss/0e_prc_id_events.nss b/src/module/nss/0e_prc_id_events.nss new file mode 100644 index 0000000..2d80300 --- /dev/null +++ b/src/module/nss/0e_prc_id_events.nss @@ -0,0 +1,317 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0e_prc_id_events +//////////////////////////////////////////////////////////////////////////////// + Infinite Dungeons monster event handler while using the PRC. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +#include "x0_i0_assoc" +// Followers special heartbeat script. +void ai_hen_id1_heart(object oCreature); +// Followers special conversation script. +void ai_hen_id1_convo(object oCreature, int nMatch); +// Followers special perception script. +void ai_hen_id1_percept(object oCreature); +// Followers special end of round script. +void ai_hen_id1_endcombat(object oCreature, int bFollower); +// Followers special castat script. +void ai_hen_id1_castat(object oCreature); + +void main() +{ + object oCreature = OBJECT_SELF; + int nEvent = GetCurrentlyRunningEvent(); + int bFollower = GetLocalInt(oCreature, "bFollower"); + switch (nEvent) + { + case EVENT_SCRIPT_CREATURE_ON_HEARTBEAT: + { + if(bFollower) + { + if(GetImmortal(oCreature)) SetImmortal(oCreature, FALSE); + ai_hen_id1_heart(oCreature); + } + else + { + ExecuteScript("nw_c2_default1", oCreature); + ExecuteScript("prc_npc_hb", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_NOTICE: + { + if(bFollower) ai_hen_id1_percept(oCreature); + else + { + ExecuteScript("nw_c2_default2", oCreature); + ExecuteScript("prc_npc_percep", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_DIALOGUE: + { + int nMatch = GetListenPatternNumber(); + if(nMatch == -1) + { + if(ai_GetIsBusy(oCreature) || ai_Disabled(oCreature) || + GetLocalInt(oCreature, AI_AM_I_SEARCHING)) return; + ai_ClearCreatureActions(); + string sConversation = GetLocalString(oCreature, "sConversation"); + if(sConversation != "") BeginConversation(sConversation); + else BeginConversation(); + } + if(bFollower) ai_hen_id1_convo(oCreature, nMatch); + else + { + //ExecuteScript("nw_c2_default4", oCreature); + ExecuteScript("prc_npc_conv", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED: + { + if(bFollower) ExecuteScript("nw_ch_ac5", oCreature); + else + { + ExecuteScript("nw_c2_default5", oCreature); + ExecuteScript("prc_npc_physatt", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_DAMAGED: + { + if(bFollower) ExecuteScript("nw_ch_ac6", oCreature); + else + { + ExecuteScript("nw_c2_default6", oCreature); + ExecuteScript("prc_npc_damaged", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT: + { + if(bFollower) ai_hen_id1_castat(oCreature); + else + { + ExecuteScript("nw_c2_defaultb", oCreature); + ExecuteScript("prc_npc_spellat", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND: + { + if(bFollower) ai_hen_id1_endcombat(oCreature, bFollower); + else + { + ExecuteScript("nw_c2_default3", oCreature); + ExecuteScript("prc_npc_combat", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR: + { + if(bFollower) ExecuteScript("nw_ch_ace", oCreature); + else + { + ExecuteScript("nw_c2_defaulte", oCreature); + ExecuteScript("prc_npc_blocked", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_RESTED: + { + if(bFollower) ExecuteScript("nw_ch_aca", oCreature); + else ExecuteScript("prc_npc_rested", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_DISTURBED: + { + if(bFollower) ExecuteScript("nw_ch_ac8", oCreature); + else + { + ExecuteScript("nw_c2_default8", oCreature); + ExecuteScript("prc_npc_disturb", oCreature); + } + break; + } + case EVENT_SCRIPT_CREATURE_ON_DEATH: + { + if(bFollower) ExecuteScript("nw_ch_ac7", oCreature); + else + { + ExecuteScript("nw_c2_default7", oCreature); + } + break; + } + } +} + +void ai_hen_id1_heart(object oCreature) +{ + // Sometimes they slip out of this mode! + if(GetAssociateState(NW_ASC_MODE_DYING, oCreature) && + GetCommandable()) + { + ActionPlayAnimation(ANIMATION_LOOPING_DEAD_FRONT, 1.0, 65.0); + SetCommandable(FALSE); + } + ExecuteScript("nw_ch_ac1", oCreature); +} +void ai_hen_id1_convo(object oCreature, int nMatch) +{ + if(nMatch == ASSOCIATE_COMMAND_INVENTORY) + { + // * cannot modify disabled equipment + if(!GetLocalInt(OBJECT_SELF, "X2_JUST_A_DISABLEEQUIP")) + { + OpenInventory(oCreature, GetLastSpeaker()); + } + // * feedback as to why + else SendMessageToPCByStrRef(GetMaster(), 100895); + return; + } + else if(nMatch == ASSOCIATE_COMMAND_LEAVEPARTY) + { + object oMaster = GetMaster(); + string sTag = GetTag(GetArea(oMaster)); + // * henchman cannot be kicked out in the reaper realm + // * Followers can never be kicked out + if (sTag == "GatesofCania" || GetIsFollower(oCreature)) return; + if(GetIsObjectValid(oMaster)) + { + ai_ClearCreatureActions(); + if(GetAssociateType(oCreature) == ASSOCIATE_TYPE_HENCHMAN) + { + string sConversation = GetLocalString(oCreature, "sConversation"); + if (sConversation == "id1_plotgiver") + { + string sVariable = GetLocalString(oCreature, "sVariable"); + object oDungeon = GetLocalObject(GetModule(), "oCurrentDungeon"); + SetLocalInt(oDungeon, "b" + sVariable + "Gone", FALSE); + } + RemoveHenchman(oMaster); + DestroyObject(oCreature); + } + } + return; + } + ExecuteScript("nw_ch_ac4", oCreature); +} +void ai_hen_id1_percept(object oCreature) +{ + // If henchman is dying and Player disappears then force a respawn of the henchman + if (GetIsHenchmanDying(oCreature)) + { + // The henchman must be removed otherwise their corpse will follow the player + object oOldMaster = GetMaster(); + object oPC = GetLastPerceived(); + int bVanish = GetLastPerceptionVanished(); + if(GetIsObjectValid(oPC) && bVanish) + { + if (oPC == oOldMaster) + { + RemoveHenchman(oPC, oCreature); + // Only in chapter 1 + if(GetTag(GetModule()) == "x0_module1") + { + SetCommandable(TRUE); + DoRespawn(oPC, oCreature); // Should teleport henchman back + } + } + } + } + ExecuteScript("nw_ch_ac2", oCreature); +} +void ai_hen_id1_endcombat(object oCreature, int bFollower) +{ + if (ai_GetIsInCombat(oCreature)) + { + int nNum; + int nLine; + string sString; + int nCreature; + int bIntelligent; + int nRandom = d100(); + // chance of a oneliner + int nOnelinerPercentage = GetLocalInt(GetModule(), "nFlagCombatOneLinerFrequencyValue"); + if(nRandom <= nOnelinerPercentage) + { + string sCreature = GetLocalString(oCreature, "sVariable"); + // if the current creature is hostile towards PCs + if(sCreature != "") + { + object oDungeon = GetLocalObject(GetModule(), "oCurrentDungeon"); + if(GetIsReactionTypeHostile(GetFirstPC())) + { + nCreature = GetLocalInt(oDungeon, "n" + sCreature); + bIntelligent = GetLocalInt(oDungeon, "bListCreature" + IntToString(nCreature) + "Intelligent"); + if(bIntelligent) + { + nNum = GetLocalInt(GetModule(), "nLinesHostileNum"); + nLine = Random(nNum) + 1; + if(nLine > 0) + { + sString = GetLocalString(GetModule(), "sLinesHostile" + IntToString(nLine)); + SpeakString(sString, TALKVOLUME_SHOUT); + } + } + } + else + { + nCreature = GetLocalInt(oDungeon, "n" + sCreature); + bIntelligent = GetLocalInt(oDungeon, "bListCreature" + IntToString(nCreature) + "Intelligent"); + if(bIntelligent) + { + nNum = GetLocalInt(GetModule(), "nLinesAlliesNum"); + nLine = Random(nNum) + 1; + if (nLine > 0) + { + sString = GetLocalString(GetModule(), "sLinesAllies" + IntToString(nLine)); + SpeakString(sString, TALKVOLUME_SHOUT); + } + } + } + } + } + } + if(bFollower) ExecuteScript("nw_ch_ac3", oCreature); + else ExecuteScript("nw_c2_default3", oCreature); +} +void ai_hen_id1_castat(object oCreature) +{ + if(!GetLastSpellHarmful()) + { + int nSpell = GetLastSpell(); + if(nSpell == SPELL_RAISE_DEAD || nSpell == SPELL_RESURRECTION) + { + object oCaster = GetLastSpellCaster(); + // Restore merchant faction to neutral + SetStandardFactionReputation(STANDARD_FACTION_MERCHANT, 100, oCaster); + SetStandardFactionReputation(STANDARD_FACTION_COMMONER, 100, oCaster); + SetStandardFactionReputation(STANDARD_FACTION_DEFENDER, 100, oCaster); + ClearPersonalReputation(oCaster, oCreature); + AssignCommand(oCreature, SurrenderToEnemies()); + AssignCommand(oCreature, ai_ClearCreatureActions(TRUE)); + // Reset henchmen attack state - Oct 28 (BK) + ai_SetAIMode(oCreature, AI_MODE_DEFEND_MASTER, FALSE); + ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE); + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE); + // Oct 30 - If player previously hired this hench + // then just have them rejoin automatically + if(GetPlayerHasHired(oCaster, oCreature)) + { + // Feb 11, 2004 - Jon: Don't fire the HireHenchman function if the + // henchman is already oCaster's associate. Fixes a silly little problem + // that occured when you try to raise a henchman who wasn't actually dead. + if(GetMaster(oCreature)!= oCaster) HireHenchman(oCaster, oCreature, TRUE); + } + else + { + string sFile = GetDialogFileToUse(oCaster); + AssignCommand(oCaster, ActionStartConversation(oCreature, sFile)); + } + } + } + ExecuteScript("nw_ch_acb", oCreature); +} diff --git a/src/module/nss/0i_actions.nss b/src/module/nss/0i_actions.nss index 7d7d2d7..586c9a1 100644 --- a/src/module/nss/0i_actions.nss +++ b/src/module/nss/0i_actions.nss @@ -152,6 +152,8 @@ void ai_AmbientAnimations(); void ai_DoAssociateCombatRound(object oCreature, object oTarget = OBJECT_INVALID) { + object oMaster = GetMaster(oCreature); + if(GetLocalInt(oMaster, AI_TARGET_MODE_ON) && GetLocalObject(oMaster, AI_TARGET_MODE_ASSOCIATE) == oCreature) return; if(ai_StayClose(oCreature)) return; // Is the target our Player has locked in dead? If so then clear it. if(GetIsDead(GetLocalObject(oCreature, AI_PC_LOCKED_TARGET))) DeleteLocalObject(oCreature, AI_PC_LOCKED_TARGET); @@ -176,21 +178,27 @@ void ai_DoAssociateCombatRound(object oCreature, object oTarget = OBJECT_INVALID // the polymorph AI script. if(sAI != "ai_coward" && sAI != "ai_a_peaceful") { - if(AI_DEBUG) ai_Debug("0i_actions", "173", "Should we use polymorph? " + - IntToString(GetAppearanceType(oCreature) != ai_GetNormalAppearance(oCreature))); if(AI_DEBUG) { + ai_Debug("0i_actions", "181", "Should we use polymorph? Current: " + + IntToString(GetAppearanceType(oCreature)) + " Normal: " + IntToString(ai_GetNormalAppearance(oCreature))); if(ai_GetIsHidden(oCreature)) { - ai_Debug("0i_actions", "179", "We are hidden!" + + ai_Debug("0i_actions", "185", "We are hidden!" + " Can they see us? " + IntToString(ai_GetNearestIndexThatSeesUs(oCreature))); } } - if(GetAppearanceType(oCreature) != ai_GetNormalAppearance(oCreature)) + if(ai_GetIsHidden(oCreature) && !ai_GetNearestIndexThatSeesUs(oCreature)) sAI = "ai_a_invisible"; + else if(GetAppearanceType(oCreature) != ai_GetNormalAppearance(oCreature)) { sAI = "ai_a_polymorphed"; + if(!GetLocalInt(oCreature, AI_POLYMORPHED)) + { + SetLocalInt(oCreature, AI_POLYMORPHED, TRUE); + ai_ClearTalents(oCreature); + ai_SetCreatureSpecialAbilityTalents(oCreature, FALSE, FALSE, FALSE); + } } - else if(ai_GetIsHidden(oCreature) && !ai_GetNearestIndexThatSeesUs(oCreature)) sAI = "ai_a_invisible"; } if(sAI == "") sAI = "ai_a_default"; if(AI_DEBUG) ai_Debug("0i_actions", "190", "********** " + GetName (oCreature) + " **********"); @@ -211,6 +219,7 @@ void ai_DoAssociateCombatRound(object oCreature, object oTarget = OBJECT_INVALID void ai_StartAssociateCombat(object oAssociate, object oTarget = OBJECT_INVALID) { if(AI_DEBUG) ai_Debug("0i_actions", "217", "---------- " + GetName(oAssociate) + " is starting combat! ----------"); + //ai_SetCreatureTalentsByLevel(oAssociate, FALSE); ai_SetCreatureTalents(oAssociate, FALSE); ai_CheckXPPartyScale(oAssociate); ai_DoAssociateCombatRound(oAssociate, oTarget); @@ -226,11 +235,24 @@ void ai_DoMonsterCombatRound(object oMonster) string sAI = GetLocalString(oMonster, AI_COMBAT_SCRIPT); if(sAI != "ai_coward") { - if(GetAppearanceType(oMonster) != ai_GetNormalAppearance(oMonster)) + if(AI_DEBUG) ai_Debug("0i_actions", "235", "Should we use polymorph? Current: " + + IntToString(GetAppearanceType(oMonster)) + " Normal: " + IntToString(ai_GetNormalAppearance(oMonster))); + if(ai_GetIsHidden(oMonster)) + { + if(AI_DEBUG) ai_Debug("0i_actions", "239", "We are hidden!" + + " Can they see us? " + IntToString(ai_GetNearestIndexThatSeesUs(oMonster))); + } + if(ai_GetIsHidden(oMonster) && !ai_GetNearestIndexThatSeesUs(oMonster)) sAI = "ai_invisible"; + else if(GetAppearanceType(oMonster) != ai_GetNormalAppearance(oMonster)) { sAI = "ai_polymorphed"; + if(!GetLocalInt(oMonster, AI_POLYMORPHED)) + { + SetLocalInt(oMonster, AI_POLYMORPHED, TRUE); + ai_ClearTalents(oMonster); + ai_SetCreatureSpecialAbilityTalents(oMonster, TRUE, FALSE, FALSE); + } } - else if(ai_GetIsHidden(oMonster) && !ai_GetNearestIndexThatSeesUs(oMonster)) sAI = "ai_invisible"; } if(sAI == "") sAI = "ai_default"; if(AI_DEBUG) ai_Debug("0i_actions", "230", "********** " + GetName (oMonster) + " **********"); @@ -277,7 +299,7 @@ int ai_StayClose(object oCreature) if(fPerceptionDistance == 0.0) { fPerceptionDistance = GetLocalFloat(oMaster, AI_ASSOC_PERCEPTION_DISTANCE); - if(fPerceptionDistance == 0.0) fPerceptionDistance = 20.0; + if(fPerceptionDistance == 0.0) fPerceptionDistance = 25.0; } object oTarget = GetLocalObject(oCreature, AI_FOLLOW_TARGET); if(oTarget == OBJECT_INVALID) oTarget = oMaster; @@ -389,9 +411,20 @@ int ai_SearchForHiddenCreature(object oCreature, int bMonster, object oInvisible } } float fPerceptionDistance, fDistance; + // Check to see if the creature is invisible because we cannot hurt them with our weapon. + // If so we need to stay away from them! Maybe add weapon swapping code later? + if(AI_DEBUG) ai_Debug("0i_actions", "415", GetName(oCreature) + "IsWeaponEffective? " + + IntToString(GetIsWeaponEffective(oInvisible)) + " oInvisible: " + GetName(oInvisible)); + /*if(!GetIsWeaponEffective(oInvisible)) + { + ai_HaveCreatureSpeak(oCreature, 20, ":21:47:7:"); + fDistance = GetDistanceBetween(oCreature, oInvisible); + if(fDistance < AI_RANGE_LONG) ActionMoveAwayFromObject(oInvisible, TRUE, AI_RANGE_LONG); + return TRUE; + } */ if(bMonster) { - GetDistanceBetween(oCreature, oInvisible); + fDistance = GetDistanceBetween(oCreature, oInvisible); fPerceptionDistance = GetLocalFloat(GetModule(), AI_RULE_PERCEPTION_DISTANCE); } else @@ -399,7 +432,7 @@ int ai_SearchForHiddenCreature(object oCreature, int bMonster, object oInvisible // We want to use the distance between the PC and target not us. object oMaster = GetMaster(); if(oMaster != OBJECT_INVALID) fDistance = GetDistanceBetween(oMaster, oInvisible); - else GetDistanceBetween(oCreature, oInvisible); + else fDistance = GetDistanceBetween(oCreature, oInvisible); fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); if(fPerceptionDistance == 0.0) fPerceptionDistance = 20.0; } @@ -512,6 +545,9 @@ int ai_MoralCheck(object oCreature) nRaceType == RACIAL_TYPE_UNDEAD || nRaceType == RACIAL_TYPE_CONSTRUCT || ai_GetIsCharacter(oCreature)) return FALSE; + int nAssociateType = GetAssociateType(oCreature); + //if(nAssociateType == ASSOCIATE_TYPE_FAMILIAR || nAssociateType == ASSOCIATE_TYPE_ANIMALCOMPANION || + // nAssociateType == ASSOCIATE_TYPE_SUMMONED) return FALSE; // Moral DC is AI_WOUNDED_MORAL_DC - The number of allies. // or AI_BLOODY_MORAL_DC - number of allies. int nDC; @@ -521,11 +557,11 @@ int ai_MoralCheck(object oCreature) if(nHpPercent <= AI_HEALTH_WOUNDED) { // Debug code to look for multiple moral checks at once by one creature? - if(GetLocalString(GetModule(), AI_RULE_DEBUG_CREATURE) == "") - { - SetLocalString(GetModule(), AI_RULE_DEBUG_CREATURE, GetName(oCreature)); - ai_Debug("0i_actions", "424", GetName(oCreature) + " starting debug mode to test Moral checks!"); - } + //if(GetLocalString(GetModule(), AI_RULE_DEBUG_CREATURE) == "") + //{ + // SetLocalString(GetModule(), AI_RULE_DEBUG_CREATURE, GetName(oCreature)); + // ai_Debug("0i_actions", "424", GetName(oCreature) + " starting debug mode to test Moral checks!"); + //} if(nHpPercent <= AI_HEALTH_BLOODY) nDC = AI_BLOODY_MORAL_DC; else nDC = AI_WOUNDED_MORAL_DC; nDC = nDC - GetLocalInt(oCreature, AI_ALLY_NUMBERS); @@ -681,17 +717,17 @@ void ai_DoPhysicalAttackOnBest(object oCreature, int nInMelee, int bAlwaysAtk = { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); } else { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; if(AI_DEBUG) ai_Debug("0i_actions", "519", "Do ranged attack against nearest: " + GetName(oTarget) + "!"); ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; @@ -739,17 +775,17 @@ void ai_DoPhysicalAttackOnNearest(object oCreature, int nInMelee, int bAlwaysAtk { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature); } else { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; if(AI_DEBUG) ai_Debug("0i_actions", "519", "Do ranged attack against nearest: " + GetName(oTarget) + "!"); ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; @@ -796,17 +832,17 @@ void ai_DoPhysicalAttackOnLowestCR(object oCreature, int nInMelee, int bAlwaysAt { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); } else { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; if(AI_DEBUG) ai_Debug("0i_actions", "559", GetName(OBJECT_SELF) + " does ranged attack on weakest: " + GetName(oTarget) + "!"); ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; @@ -1112,7 +1148,7 @@ void ai_ActionTryHealing(object oCreature, object oTarget) } int ai_TryHealing(object oCreature, object oTarget, int bForce = FALSE) { - if(AI_DEBUG) ai_Debug("0i_actions", "733", "Try healing: oCreature: " + GetName(oCreature) + + if(AI_DEBUG) ai_Debug("0i_actions", "1136", "Try healing: oCreature: " + GetName(oCreature) + " oTarget: " + GetName(oTarget) + " No Party Healing: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_PARTY_HEALING_OFF)) + " No Self Healing: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_SELF_HEALING_OFF)) + " AI_I_AM_BEING_HEALED: " + IntToString(GetLocalInt(oTarget, "AI_I_AM_BEING_HEALED")) + @@ -1133,52 +1169,92 @@ int ai_TryHealing(object oCreature, object oTarget, int bForce = FALSE) oCreature != oTarget) return FALSE; if(ai_GetAIMode(oCreature, AI_MODE_SELF_HEALING_OFF) && oCreature == oTarget) return FALSE; - // Undead don't heal so lets skip this for them, maybe later we can fix this. - if(GetRacialType(oTarget) == RACIAL_TYPE_UNDEAD) return FALSE; int nHpLost = ai_GetPercHPLoss(oTarget); + // If the player is forcing a heal then we always heal. if(bForce && nHpLost < 100) nHpLost = 0; - if(AI_DEBUG) ai_Debug("0i_actions", "743", "nHpLost: " + IntToString(nHpLost) + + if(AI_DEBUG) ai_Debug("0i_actions", "1160", "nHpLost: " + IntToString(nHpLost) + " limit: " + IntToString(ai_GetHealersHpLimit(oTarget, FALSE))); - if(nHpLost >= ai_GetHealersHpLimit(oTarget, FALSE)) + // Check to see if we need poison, disease, or ability drain removed. + int nEffectType; + effect eEffect = GetFirstEffect(oTarget); + while(GetIsEffectValid(eEffect)) { - // Check to see if we need poison, disease, or ability drain removed. - int nEffectType; - effect eEffect = GetFirstEffect(oTarget); - while(GetIsEffectValid(eEffect)) + nEffectType = GetEffectType(eEffect); + if(AI_DEBUG) ai_Debug("0i_actions", "1168", "Checking to cure(31/32/39) nEffectType: " + IntToString(nEffectType)); + if(nEffectType == EFFECT_TYPE_DISEASE) { - nEffectType = GetEffectType(eEffect); - if(AI_DEBUG) ai_Debug("0i_actions", "1094", "Checking to cure(31/32/39) nEffectType: " + IntToString(nEffectType)); - if(nEffectType == EFFECT_TYPE_DISEASE) + if(AI_DEBUG) ai_Debug("0i_actions", "1171", GetName(oTarget) + " is diseased!"); + if(ai_HealSickness(oCreature, oTarget, ai_GetPlayerMaster(oCreature), AI_ALLY_IS_DISEASED, bForce)) return TRUE; + if(oCreature == oTarget) { - if(AI_DEBUG) ai_Debug("0i_actions", "1097", "I am diseased!"); - if(ai_HealSickness(oCreature, oTarget, ai_GetPlayerMaster(oCreature), AI_ALLY_IS_DISEASED, bForce)) return TRUE; - if(oCreature == oTarget) - { - if(!d20()) ai_HaveCreatureSpeak(oCreature, 5, ":43:4:14:15:16:"); - SpeakString(AI_I_AM_DISEASED, TALKVOLUME_SILENT_TALK); - } + if(!Random(20)) ai_HaveCreatureSpeak(oCreature, 5, ":43:4:14:15:16:"); + SpeakString(AI_I_AM_DISEASED, TALKVOLUME_SILENT_TALK); } - else if(nEffectType == EFFECT_TYPE_POISON) + } + else if(nEffectType == EFFECT_TYPE_POISON) + { + if(AI_DEBUG) ai_Debug("0i_actions", "1181", GetName(oTarget) + " is poisoned!"); + if(ai_HealSickness(oCreature, oTarget, ai_GetPlayerMaster(oCreature), AI_ALLY_IS_POISONED, bForce)) return TRUE; + if(oCreature == oTarget) { - if(AI_DEBUG) ai_Debug("0i_actions", "1107", "I am poisoned!"); - if(ai_HealSickness(oCreature, oTarget, ai_GetPlayerMaster(oCreature), AI_ALLY_IS_POISONED, bForce)) return TRUE; - if(oCreature == oTarget) - { - if(!d20()) ai_HaveCreatureSpeak(oCreature, 6, ":43:4:14:15:16:19:"); - SpeakString(AI_I_AM_POISONED, TALKVOLUME_SILENT_TALK); - } + if(!Random(20)) ai_HaveCreatureSpeak(oCreature, 6, ":43:4:14:15:16:19:"); + SpeakString(AI_I_AM_POISONED, TALKVOLUME_SILENT_TALK); } - else if(nEffectType == EFFECT_TYPE_ABILITY_DECREASE) + } + else if(nEffectType == EFFECT_TYPE_ABILITY_DECREASE) + { + if(AI_DEBUG) ai_Debug("0i_actions", "1191", GetName(oTarget) + " is weak!"); + if(ai_HealSickness(oCreature, oTarget, ai_GetPlayerMaster(oCreature), AI_ALLY_IS_WEAK, bForce)) return TRUE; + if(oCreature == oTarget) { - if(AI_DEBUG) ai_Debug("0i_actions", "1117", "I am weak!"); - if(ai_HealSickness(oCreature, oTarget, ai_GetPlayerMaster(oCreature), AI_ALLY_IS_WEAK, bForce)) return TRUE; - if(oCreature == oTarget) - { - if(!d20()) ai_HaveCreatureSpeak(oCreature, 3, ":43:4:5:"); - SpeakString(AI_I_AM_WEAK, TALKVOLUME_SILENT_TALK); - } + if(!Random(20)) ai_HaveCreatureSpeak(oCreature, 3, ":43:4:5:"); + SpeakString(AI_I_AM_WEAK, TALKVOLUME_SILENT_TALK); } - eEffect = GetNextEffect(oTarget); + } + eEffect = GetNextEffect(oTarget); + } + // Everything below here is for healing. + if(nHpLost >= ai_GetHealersHpLimit(oTarget, FALSE)) return FALSE; + // Undead require inflict spells to heal! + object oMaster = ai_GetPlayerMaster(oCreature); + if(GetRacialType(oTarget) == RACIAL_TYPE_UNDEAD) + { + // Do we have no magic on? + if(!ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) + { + int nClass, nPosition = 1; + string sMemorized; + while(nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + nClass = GetClassByPosition(nPosition, oCreature); + if(AI_DEBUG) ai_Debug("0i_actions", "753", "nClass: " + IntToString(nClass)); + if(nClass == CLASS_TYPE_INVALID) break; + sMemorized = Get2DAString("classes", "MemorizesSpells", nClass); + // If Memorized column is "" then they are not a caster. + if(sMemorized != "") + { + if(sMemorized == "1") + { + if(ai_CastMemorizedInflict(oCreature, oTarget, oMaster, nClass)) + { + SetLocalInt(oTarget, "AI_I_AM_BEING_HEALED", TRUE); + return TRUE; + } + } + else if(ai_CastKnownInflict(oCreature, oTarget, oMaster, nClass)) + { + SetLocalInt(oTarget, "AI_I_AM_BEING_HEALED", TRUE); + return TRUE; + } + } + nPosition++; + } + } + // We can't heal ourselves! Can any of our allies? Lets ask. + if(oCreature == oTarget) + { + SetLocalInt(oCreature, "AI_WOUNDED_SHOUT_LIMIT", GetLocalInt(oCreature, "AI_WOUNDED_SHOUT_LIMIT") + 1); + SpeakString(AI_I_AM_WOUNDED, TALKVOLUME_SILENT_TALK); } return FALSE; } @@ -1192,7 +1268,6 @@ int ai_TryHealing(object oCreature, object oTarget, int bForce = FALSE) return TRUE; } } - object oMaster = ai_GetPlayerMaster(oCreature); // Do we have no magic on? if(!ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) { diff --git a/src/module/nss/0i_associates.nss b/src/module/nss/0i_associates.nss index 2309160..8bc1567 100644 --- a/src/module/nss/0i_associates.nss +++ b/src/module/nss/0i_associates.nss @@ -52,6 +52,8 @@ void ai_OnRested(object oCreature); // Increments/Decrements the following distance of associates. void ai_FollowIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType); +// Increments/Decrements the delay when casting each auto buff spell. +void ai_DelaySpellSpeed(object oPC, object oAssociate, float fIncrement, string sAssociateType); // Turns on/off Ranged combat for oAssociate. void ai_Ranged(object oPC, object oAssociate, string sAssociateType); // Turns on/off Ignore enemy associates for oAssociate. @@ -115,7 +117,7 @@ void ai_Philos_SetStealth(object oMaster, object oCreature, string sAssociateTyp // Button action for giving commands to associates. void ai_DoCommand(object oPC, object oAssociate, int nCommand); // Button action to have associate do an action based on the target via OnPlayer Target event. -void ai_Action(object oPC, object oAssociate); +void ai_Action(object oPC, object oAssociate, int bPCAI = FALSE); // Toggles between normal ai script and special tactic ai scripts. void ai_AIScript(object oPC, object oAssociate, string sAssociate, int nToken); // Has the PC select a Trap and then place it on the ground from an associate. @@ -130,6 +132,10 @@ void ai_ChangeCameraView(object oPC, object oAssociate); void ai_OpenInventory(object oAssociate, object oPC); // Executes an installed plugin. void ai_Plugin_Execute(object oPC, string sElem, int bUser = 0); +// Turns on oAssociate AI, Setting all event scripts. +void ai_TurnOn(object oPC, object oAssociate, string sAssociateType); +// Turns off oAssociate AI, Setting all event scripts. +void ai_TurnOff(object oPC, object oAssociate, string sAssociateType); int ai_CanIAttack(object oCreature) { @@ -161,7 +167,7 @@ object ai_GetNearestLockedObject(object oCreature) void ai_FindTheEnemy(object oCreature, object oSpeaker, object oTarget, int bMonster) { if(GetLocalInt(oCreature, AI_AM_I_SEARCHING)) return; - if(oSpeaker == oTarget && d100() < 34) + if(oSpeaker == oTarget && d100() < 16) { // Let them know we heard something in the distance!. if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) @@ -232,7 +238,7 @@ void ai_FindTheEnemy(object oCreature, object oSpeaker, object oTarget, int bMon if(AI_DEBUG) ai_Debug("0i_associates", "176", "Moving and searching for " + GetName(oTarget)); SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE); ActionMoveToLocation(GetLocation(oTarget), FALSE); - //ActionMoveToObject(oTarget, FALSE, AI_RANGE_MELEE); + //ActionMoveToObject(oTarget, FALSE, AI_RANGE_MELEE); AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING))); return; } @@ -519,6 +525,24 @@ void ai_SelectAssociateCommand(object oCreature, object oCommander, int nCommand ai_FireHenchman (GetPCSpeaker(), oCreature); PlayVoiceChat (VOICE_CHAT_GOODBYE, oCreature); } + else if(AI_PATROL_AHEAD_RADIAL_OPTION) + { + if(ai_GetAIMode(oCreature, AI_MODE_SCOUT_AHEAD)) + { + ai_ClearCreatureActions(); + ai_HaveCreatureSpeak(oCreature, 6, ":29:35:46:10"); + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SendMessages(GetName(oCreature) + " has stopped patrolling ahead.", AI_COLOR_YELLOW, oMaster); + } + else + { + ai_ClearCreatureActions(); + ai_HaveCreatureSpeak(oCreature, 6, ":29:35:46:22:"); + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, TRUE); + ai_SendMessages(GetName(oCreature) + " is now patrolling ahead.", AI_COLOR_YELLOW, oMaster); + ai_ScoutAhead(oCreature); + } + } } } } @@ -769,7 +793,7 @@ void ai_AssociateEvaluateNewThreat(object oCreature, object oLastPerceived, stri if(sPerception == AI_I_SEE_AN_ENEMY || GetObjectSeen(oLastPerceived, oCreature)) { // We are not in combat and we see the enemy so alert our allies! - ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:"); + ai_HaveCreatureSpeak(oCreature, 10, ":0:1:2:3:6:"); SetLocalObject (oCreature, AI_MY_TARGET, oLastPerceived); SpeakString(sPerception, TALKVOLUME_SILENT_TALK); ai_StartAssociateCombat(oCreature); @@ -832,10 +856,10 @@ void ai_MonsterEvaluateNewThreat(object oCreature, object oLastPerceived, string if(d100() < 34) { // We are not in combat so alert our allies! - ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:"); + ai_HaveCreatureSpeak(oCreature, 10, ":0:1:2:3:6:"); } SetLocalObject(oCreature, AI_MY_TARGET, oLastPerceived); - SpeakString(sPerception, TALKVOLUME_SILENT_TALK); + SpeakString(AI_I_SEE_AN_ENEMY, TALKVOLUME_SILENT_TALK); ai_StartMonsterCombat(oCreature); } else ai_FindTheEnemy(oCreature, oLastPerceived, oLastPerceived, TRUE); @@ -919,6 +943,23 @@ void ai_FollowIncrement(object oPC, object oAssociate, float fIncrement, string ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]"); } } +void ai_DelaySpellSpeed(object oPC, object oAssociate, float fIncrement, string sAssociateType) +{ + float fAdjustment = GetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING) + fIncrement; + if(fAdjustment > 6.0) fAdjustment = 6.0; + else if(fAdjustment < 0.1) fAdjustment = 0.1; + SetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING, fAdjustment); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 11, JsonFloat(fAdjustment)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + string sDelay = FloatToString(fAdjustment, 0, 1); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, + "btn_buff_long_tooltip", " Buff the party with long duration spells. Cast speed [" + sDelay + "]"); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, + "btn_buff_short_tooltip", " Buff the party with short duration spells. Cast speed [" + sDelay + "]"); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, + "btn_buff_all_tooltip", " Buff the party with all spells. Cast speed [" + sDelay + "]"); +} void ai_Ranged(object oPC, object oAssociate, string sAssociateType) { //ai_ClearCreatureActions(); @@ -1903,18 +1944,20 @@ void ai_DoCommand(object oPC, object oAssociate, int nCommand) } } } -void ai_Action(object oPC, object oAssociate) +void ai_Action(object oPC, object oAssociate, int bPCAI = FALSE) { if(oPC == oAssociate) { - DeleteLocalObject(oPC, "NW_ASSOCIATE_COMMAND"); SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION_ALL"); ai_SendMessages("Select an action for the party.", AI_COLOR_YELLOW, oPC); } else { SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate); + SetLocalObject(oPC, AI_TARGET_MODE_ASSOCIATE, oAssociate); SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION"); + SetLocalInt(oPC, AI_TARGET_MODE_ON, TRUE); + if(!GetLocalInt(GetModule(), AI_USING_PRC) && bPCAI) ai_TurnOn(oPC, oPC, "pc"); ai_SendMessages("Select an action for " + GetName(oAssociate) + ".", AI_COLOR_YELLOW, oPC); } EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); @@ -2139,6 +2182,7 @@ void ai_ChangeCameraView(object oPC, object oAssociate) { SetLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE", oAssociate); AttachCamera(oPC, oAssociate); + if(!ai_GetIsCharacter(oAssociate)) ai_Action(oPC, oAssociate, TRUE); } } void ai_SelectCameraView(object oPC) @@ -2190,3 +2234,47 @@ void ai_Plugin_Execute(object oPC, string sElem, int bUser = 0) ExecuteScript(sScript, oPC); } } +void ai_TurnOn(object oPC, object oTarget, string sAssociateType) +{ + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ai_tooltip", " AI On"); + ai_SendMessages("AI turned on for " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "xx_pc_1_hb"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_NOTICE, "xx_pc_2_percept"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "xx_pc_3_endround"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "xx_pc_4_convers"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "xx_pc_5_phyatked"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "xx_pc_6_damaged"); + //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DEATH, ""); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "xx_pc_8_disturb"); + //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_RESTED, ""); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "xx_pc_b_castat"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "xx_pc_e_blocked"); + //SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); + // This sets the script for the PC to run AI based on class. + ai_SetAssociateAIScript(oTarget, FALSE); + // Set so PC can hear associates talking in combat. + ai_SetListeningPatterns(oTarget); +} +void ai_TurnOff(object oPC, object oAssociate, string sAssociateType) +{ + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ai_tooltip", " AI Off"); + ai_SendMessages("AI Turned off for " + GetName(oAssociate) + ".", AI_COLOR_YELLOW, oPC); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_NOTICE, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DAMAGED, ""); + //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DEATH, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_DISTURBED, ""); + //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_RESTED, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, ""); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, ""); + //SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); + DeleteLocalInt(oAssociate, "AI_I_AM_BEING_HEALED"); + DeleteLocalString(oAssociate, "AIScript"); + ai_ClearCreatureActions(); +} + diff --git a/src/module/nss/0i_combat.nss b/src/module/nss/0i_combat.nss index 6c40eb1..1133627 100644 --- a/src/module/nss/0i_combat.nss +++ b/src/module/nss/0i_combat.nss @@ -39,6 +39,7 @@ struct stTarget //****************************************************************************** //************ GET TARGETS USING THE OBJECT SEARCH FUNCTIONS ******************* //****************************************************************************** + // Returns the nearest enemy that is not disabled from oCreature. // You may pass in any of the CREATURE_TYPE_* constants // used in GetNearestCreature as nCType1 & nCType2, with @@ -64,11 +65,14 @@ object ai_GetLowestCRAttackerOnMaster(object oCreature); //****************************************************************************** //******************** SET/CLEAR COMBAT STATE FUNCTIONS ************************ //****************************************************************************** + // Sets oCreatures's combat state by setting variables for AI_ALLIES and AI_ENEMIES. // Returns the nearest visible enemy. object ai_SetCombatState(object oCreature); // Clears all variables that were define for the current round for oCreature. void ai_ClearCombatState(object oCreature); +// Clears just the Talent variables so we can recalculate the Talents. +void ai_ClearTalents(object oCreature); //****************************************************************************** //*************** GET TARGETS USING COMBAT STATE FUNCTIONS ********************* @@ -76,51 +80,187 @@ void ai_ClearCombatState(object oCreature); // These functions will find a target or an index to a target based on the // combat state variables created by the function ai_SetCombatState. +// ******************************** Target Checks ****************************** +// ** Used to get creatures within fMaxRange as a target. ** +// ** These checks that do basic checks to find a creature. ** +// ** Used mostly in spell checks. ** +// ***************************************************************************** // Returns the Index of the nearest creature seen within fMaxRange in the combat state. // If no creature is found then it will return an index of 0. // sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); // Returns the nearest creature seen within fMaxRange in the combat state. // Returns OBJECT_INVALID if no creature is found. // sTargetType is either AI_ENEMY or AI_ALLY. -object ai_GetNearestTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +object ai_GetNearestTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); // Returns the index of the nearest creature seen with the lowest combat rating // within fMaxRange in the combat state. // If no creature is found then it will return an index of 0. // sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); // Returns the nearest creature seen with the lowest combat rating within fMaxRange // in the combat state. // Returns OBJECT_INVALID if no creature is found. // sTargetType is either AI_ENEMY or AI_ALLY. -object ai_GetLowestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +object ai_GetLowestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); // Returns the index of the nearest creature seen with the highest combat rating // within fMaxRange in the combat state. // If no creature is found then it will return an index of 0. // sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); // Returns the nearest creature seen with the highest combat rating within fMaxRange // in the combat state. // Returns OBJECT_INVALID if no creature is found. // sTargetType is either AI_ENEMY or AI_ALLY. -object ai_GetHighestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); -// Returns the index of the creature seen with the lowest enemies to oCreature that -// they are in melee with minus the number of allies to the caller they are in -// melee with within fMaxRange in the combat state. +object ai_GetHighestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); + +// *************************** Not in an AOE Target Checks ********************* +// ** Used to get creatures within fMaxRange as a target. ** +// ** These checks that do basic checks to find a creature that is not in a ** +// ** harmful AOE. Used in spell checks to target unaffected cratures first. ** +// ***************************************************************************** +// Returns the index of the nearest creature seen that is busy attacking an ally +// within fMaxRange in the combat state. +// If none is found then it will return 0. +int ai_GetNearestIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns the nearest combat creature seen within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetNearestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns the index of the nearest creature seen with the lowest combat rating +// that is not in a dangerous area of effect within fMaxRange in the combat state. // If no creature is found then it will return an index of 0. // sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetLowestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); -// Returns the index of the creature seen with the most enemies to the caller that -// they are in melee with minus the number of allies to oCreature they are in -// melee with within fMaxRange in the combat state. +int ai_GetLowestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns the lowest combat creature seen within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetLowestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns the index of the nearest creature seen with the highest combat rating +// that is not in a dangerous area of effect within fMaxRange in the combat state. // If no creature is found then it will return an index of 0. // sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetHighestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +int ai_GetHighestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns the highest combat creature seen within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetHighestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); + +// ******************************** Physical Target Checks ********************* +// ** Used in melee checks to make sure the attacker can damage oCreature ** +// ** as well as stay within range of their master. ** +// ***************************************************************************** +// Returns the Index of the nearest creature seen within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetNearestPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the nearest creature seen within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetNearestPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the index of the nearest creature seen with the lowest combat rating +// within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetLowestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the nearest creature seen with the lowest combat rating within fMaxRange +// in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetLowestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the index of the nearest creature seen with the highest combat rating +// within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetHighestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the nearest creature seen with the highest combat rating within fMaxRange +// in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetHighestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); + +// *************************** Not in an AOE Target Checks ********************* +// ** Used to get creatures within fMaxRange as a target. ** +// ** These checks that do basic checks to find a creature that is not in a ** +// ** harmful AOE. Used in spell checks to target unaffected cratures first. ** +// ***************************************************************************** +// Returns the index of the nearest creature seen that is busy attacking an ally +// within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// If none is found then it will return 0. +int ai_GetNearestPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the nearest combat creature seen within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetNearestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the index of the nearest creature seen with the lowest combat rating +// that is not in a dangerous area of effect within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetLowestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the lowest combat creature seen within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetLowestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the index of the nearest creature seen with the highest combat rating +// that is not in a dangerous area of effect within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetHighestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the highest combat creature seen within fMaxRange in the combat state. +// This also checks if the caller can do damage to oCreature with current weapon. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +object ai_GetHighestCRPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); + +// ******************************** Group Target Checks ************************ +// ** These get the largest group of enemies in 5' with a minimal number of ** +// ** allies within fMaxRange. Used for AOE spells. ** +// ***************************************************************************** +// Returns the index of the creature seen with the lowest enemies to oCreature +// that are in a group minus the number of allies in that group within +// fMaxRange in the combat state. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetLowestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns the index of the creature seen with the most enemies to the caller +// that are in a group minus the number of allies in that group within +// fMaxRange in the combat state. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetHighestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); // Returns a creature of sTargetType where they have the least number of // allies and the most number of enemies within fMaxRange in the combat state. // Returns OBJECT_INVALID if there is not a good creature to select. // sTargetType is either AI_ENEMY, or AI_ALLY. object ai_GetGroupedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns the index of the creature seen with the most enemies to oCreature that +// they are in melee with minus the number of allies to oCreature they are in +// melee with that is not in a dangerous area of effect within fMaxRange in the combat state. +// If no creature is found then it will return an index of 0. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetHighestGroupIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); +// Returns a creature of sTargetType where they have the least number of +// allies and the most number of enemies within fMaxRange that are not in a +// dangerous area of effect in the combat state. +// Returns OBJECT_INVALID if there is not a good creature to select. +// sTargetType is either AI_ENEMY, or AI_ALLY. +object ai_GetGroupTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); + +// ***************************** Specific Target Checks ************************ +// ** Used in melee checks to make sure the attacker can damage oCreature ** +// ** as well as stay within range of their master. ** +// ***************************************************************************** // Returns the index of the nearest creature with the least % of hitpoints within // fMaxRange in the combat state. // If no creature is found then it will return an index of 0. @@ -155,44 +295,6 @@ object ai_GetSpellTargetBasedOnSaves(object oCreature, int nSpell, float fMaxRan // within fMaxRange in the combat state. // If none is found then it will return 0. int ai_GetSneakAttackIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE); -// Returns the index of the nearest creature seen that is busy attacking an ally -// within fMaxRange in the combat state. -// If none is found then it will return 0. -int ai_GetNearestIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); -// Returns the nearest combat creature seen within fMaxRange in the combat state. -// Returns OBJECT_INVALID if no creature is found. -// sTargetType is either AI_ENEMY or AI_ALLY. -object ai_GetNearestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); -// Returns the index of the nearest creature seen with the lowest combat rating -// that is not in a dangerous area of effect within fMaxRange in the combat state. -// If no creature is found then it will return an index of 0. -// sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetLowestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); -// Returns the lowest combat creature seen within fMaxRange in the combat state. -// Returns OBJECT_INVALID if no creature is found. -// sTargetType is either AI_ENEMY or AI_ALLY. -object ai_GetLowestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); -// Returns the index of the nearest creature seen with the highest combat rating -// that is not in a dangerous area of effect within fMaxRange in the combat state. -// If no creature is found then it will return an index of 0. -// sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetHighestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); -// Returns the highest combat creature seen within fMaxRange in the combat state. -// Returns OBJECT_INVALID if no creature is found. -// sTargetType is either AI_ENEMY or AI_ALLY. -object ai_GetHighestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); -// Returns the index of the creature seen with the most enemies to oCreature that -// they are in melee with minus the number of allies to oCreature they are in -// melee with that is not in a dangerous area of effect within fMaxRange in the combat state. -// If no creature is found then it will return an index of 0. -// sTargetType is either AI_ENEMY or AI_ALLY. -int ai_GetHighestMeleeIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); -// Returns a creature of sTargetType where they have the least number of -// allies and the most number of enemies within fMaxRange that are not in a -// dangerous area of effect in the combat state. -// Returns OBJECT_INVALID if there is not a good creature to select. -// sTargetType is either AI_ENEMY, or AI_ALLY. -object ai_GetGroupedTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY); // Returns the nearest creature seen of nClassType within fMaxRange in the combat state. // Returns OBJECT_INVALID if no creature is found. // sTargetType is either AI_ENEMY or AI_ALLY. @@ -376,6 +478,20 @@ int ai_CanIUseRangedWeapon(object oCreature, int nInMelee); // and -1 if the action is canceled. // Checks current combat state to see if oCreature needs to move before using an action. int ai_CheckCombatPosition(object oCreature, object oTarget, int nInMelee, int nAction, int nBaseItemType = 0); +//:: [PRC8] Calculates total Shield AC bonuses from all sources +int ai_GetTotalShieldACBonus(object oCreature); +//:: [PRC8] Helper fucntion for CanIGrapple() +int GetSpecialGrappleSizeMod(int nSize); +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Tripping +int ai_CanITrip(object oCreature, object oTarget); +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Grappling +int ai_CanIGrapple(object oCreature, object oTarget); +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Awesome Blow +int ai_CanIAwesomeBlow(object oCreature, object oTarget); +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Overrun +int ai_CanIOverrun(object oCreature, object oTarget); +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Bullrush +int ai_CanIBullrush(object oCreature, object oTarget); //****************************************************************************** //************ GET TARGETS USING THE OBJECT SEARCH FUNCTIONS ******************* @@ -458,7 +574,7 @@ int ai_GetRacialTypeCount(object oCreature, int nRacial_Type, float fDistance = object ai_GetLowestCRAttackerOnMaster(object oCreature) { object oTarget = OBJECT_INVALID, oMaster = GetMaster(oCreature); - if(AI_DEBUG) ai_Debug("0i_combat", "419", "Checking for weakest attacker on " + GetName(oMaster)); + if(AI_DEBUG) ai_Debug("0i_combat", "577", "Checking for weakest attacker on " + GetName(oMaster)); int nEnemyCombatRating, nWeakestCombatRating, nCntr = 1; float fNearest = AI_RANGE_MELEE + 1.0f; // Get the weakest opponent in melee with our master. @@ -467,7 +583,7 @@ object ai_GetLowestCRAttackerOnMaster(object oCreature) while (oEnemy != OBJECT_INVALID && fDistance <= AI_RANGE_MELEE) { nEnemyCombatRating = ai_GetMyCombatRating(oEnemy); - if(AI_DEBUG) ai_Debug("0i_combat", "428", GetName(oEnemy) + " nECR: " + IntToString(nEnemyCombatRating)); + if(AI_DEBUG) ai_Debug("0i_combat", "586", GetName(oEnemy) + " nECR: " + IntToString(nEnemyCombatRating)); if (nEnemyCombatRating < nWeakestCombatRating || nEnemyCombatRating == nWeakestCombatRating && fDistance < fNearest) { @@ -478,7 +594,13 @@ object ai_GetLowestCRAttackerOnMaster(object oCreature) oEnemy = ai_GetNearestEnemy(oMaster, ++nCntr, 7, 7); } // No targets in melee with our master, lets see if there is a ranged attacker. - if (oTarget == OBJECT_INVALID) oTarget = GetLastHostileActor(oMaster); + if (oTarget == OBJECT_INVALID) + { + oTarget = GetLastHostileActor(oMaster); + // Sometimes things happen and our own associates may hurt the player too! + if(ai_GetTopMaster(oTarget) == oMaster) oTarget = OBJECT_INVALID; + } + if(AI_DEBUG) ai_Debug("0i_combat", "598", "Attacking master: " + GetName(oTarget)); return oTarget; } @@ -801,8 +923,31 @@ void ai_ClearCombatState(object oCreature) DeleteLocalInt(oCreature, sIPImmuneVarname); DeleteLocalInt(oCreature, sIPResistVarname); DeleteLocalInt(oCreature, sIPReducedVarname); + DeleteLocalJson(oCreature, AI_TALENT_IMMUNITY); ai_EndCombatRound(oCreature); } +void ai_ClearTalents(object oCreature) +{ + // Remove Talent variables. + DeleteLocalJson(oCreature, AI_TALENT_CURE); + DeleteLocalJson(oCreature, AI_TALENT_HEALING); + DeleteLocalJson(oCreature, AI_TALENT_ENHANCEMENT); + DeleteLocalJson(oCreature, AI_TALENT_PROTECTION); + DeleteLocalJson(oCreature, AI_TALENT_SUMMON); + DeleteLocalJson(oCreature, AI_TALENT_DISCRIMINANT_AOE); + DeleteLocalJson(oCreature, AI_TALENT_INDISCRIMINANT_AOE); + DeleteLocalJson(oCreature, AI_TALENT_RANGED); + DeleteLocalJson(oCreature, AI_TALENT_TOUCH); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_HEALING); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_ENHANCEMENT); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_PROTECTION); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_SUMMON); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_DISCRIMINANT_AOE); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_INDISCRIMINANT_AOE); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_RANGED); + DeleteLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_TOUCH); +} //****************************************************************************** //*********************** GET TARGETS INTERNAL FUNCTIONS *********************** //****************************************************************************** @@ -810,7 +955,7 @@ void ai_ClearCombatState(object oCreature) int ai_TargetIsInRangeofCreature(object oCreature, string sTargetType, string sCounter, float fMaxRange) { - if(AI_DEBUG) ai_Debug("0i_combat", "796", "fMaxRange: " + FloatToString(fMaxRange, 0, 2) + + if(AI_DEBUG) ai_Debug("0i_combat", "952", "fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " fTargetRange: " + FloatToString(GetLocalFloat(oCreature, sTargetType + "_RANGE" + sCounter), 0, 2)); return fMaxRange >= GetLocalFloat(oCreature, sTargetType + "_RANGE" + sCounter); } @@ -821,13 +966,13 @@ int ai_TargetIsInRangeofMaster(object oCreature, object oTarget) float fMaxRange = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); if(fMaxRange == 0.0) fMaxRange = 20.0; float fTargetRangefromMaster = GetDistanceBetween(oTarget, oMaster); - if(AI_DEBUG) ai_Debug("0i_combat", "807", "fMaxRangefromMaster: " + FloatToString(fMaxRange, 0, 2) + + if(AI_DEBUG) ai_Debug("0i_combat", "963", "fMaxRangefromMaster: " + FloatToString(fMaxRange, 0, 2) + " fTargetRangefromMaster: " + FloatToString(fTargetRangefromMaster, 0, 2)); return fMaxRange >= fTargetRangefromMaster; } struct stTarget ai_CheckForNearestTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) { - if(AI_DEBUG) ai_Debug("0i_combat", "817", "Getting nearest index: " + sIndex + + if(AI_DEBUG) ai_Debug("0i_combat", "969", "Getting nearest index: " + sIndex + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) + " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2)); @@ -850,7 +995,7 @@ struct stTarget ai_CheckForNearestTarget(object oCreature, struct stTarget sTarg } struct stTarget ai_CheckForLowestValueTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) { - if(AI_DEBUG) ai_Debug("0i_combat", "835", "Getting lowest value index: " + sIndex + + if(AI_DEBUG) ai_Debug("0i_combat", "992", "Getting lowest value index: " + sIndex + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + " fNearestRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2) + @@ -883,7 +1028,7 @@ struct stTarget ai_CheckForLowestValueTarget(object oCreature, struct stTarget s } struct stTarget ai_CheckForHighestValueTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) { - if(AI_DEBUG) ai_Debug("0i_combat", "865", "Getting highest value index: " + sIndex + + if(AI_DEBUG) ai_Debug("0i_combat", "1025", "Getting highest value index: " + sIndex + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) + " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2) + @@ -916,12 +1061,12 @@ struct stTarget ai_CheckForHighestValueTarget(object oCreature, struct stTarget } struct stTarget ai_CheckForNearestAllTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) { - if(AI_DEBUG) ai_Debug("0i_combat", "895", "Getting nearest (not disabled) index: " + sIndex + + if(AI_DEBUG) ai_Debug("0i_combat", "1153", "Getting nearest (not disabled) index: " + sIndex + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2)); // If we are ignoring associates set then ignore them. // Has lower value or equal value and is closer. Familiars/Companions/Summons/Dominated. - if(AI_DEBUG) ai_Debug("0i_combat", "911", "Don't Ignore Associate: " + IntToString(!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES)) + + if(AI_DEBUG) ai_Debug("0i_combat", "1158", "Don't Ignore Associate: " + IntToString(!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES)) + " Not an Associate? " + IntToString(GetAssociateType(sTarget.oTarget) < 2)); if((!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) || GetAssociateType(sTarget.oTarget) < 2) && GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange) @@ -933,13 +1078,13 @@ struct stTarget ai_CheckForNearestAllTarget(object oCreature, struct stTarget sT } struct stTarget ai_CheckForLowestValueAllTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) { - if(AI_DEBUG) ai_Debug("0i_combat", "923", "Getting lowest value index: " + sIndex + + if(AI_DEBUG) ai_Debug("0i_combat", "1170", "Getting lowest value index: " + sIndex + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) + " sTarget.nValue: " + IntToString(sTarget.nValue) + " sTarget.nBestValue: " + IntToString(sTarget.nBestValue)); // Has less value or equal value and is closer. Ignoring only Familiars/Companions/Summons/Dominated. - if(AI_DEBUG) ai_Debug("0i_combat", "922", "Don't Ignore Associate: " + IntToString(!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES)) + + if(AI_DEBUG) ai_Debug("0i_combat", "1176", "Don't Ignore Associate: " + IntToString(!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES)) + " Not an Associate? " + IntToString(GetAssociateType(sTarget.oTarget) < 2)); if((!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) || GetAssociateType(sTarget.oTarget) < 2) && sTarget.nValue < sTarget.nBestValue || @@ -952,18 +1097,112 @@ struct stTarget ai_CheckForLowestValueAllTarget(object oCreature, struct stTarge } return sTarget; } - +struct stTarget ai_CheckForNearestPhysicalTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "969", "Getting nearest physical index: " + sIndex + + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + + " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) + + " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); + // Lets put any disabled targets and associates if set in a secondary group. + if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) || + !GetIsWeaponEffective(sTarget.oTarget) || + (ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && GetAssociateType(sTarget.oTarget))) + { + if(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestSecondaryRange) + { + sTarget.fNearestSecondaryRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex); + sTarget.nSecondaryIndex = nIndex; + } + } + else if(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange) + { + sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex); + sTarget.nIndex = nIndex; + } + return sTarget; +} +struct stTarget ai_CheckForLowestValuePhysicalTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1126", "Getting lowest value physcial index: " + sIndex + + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + + " fNearestRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + + " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2) + + " sTarget.nValue: " + IntToString(sTarget.nValue) + + " sTarget.nBestValue: " + IntToString(sTarget.nBestValue) + + " sTarget.nBestSecondaryValue: " + IntToString(sTarget.nBestSecondaryValue) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); + // Lets put any disabled targets and associates if set in a secondary group. + if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) || + !GetIsWeaponEffective(sTarget.oTarget) || + (ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && GetAssociateType(sTarget.oTarget))) + { + if(sTarget.nValue < sTarget.nBestSecondaryValue || + (sTarget.nValue == sTarget.nBestSecondaryValue && + GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestSecondaryRange)) + { + sTarget.fNearestSecondaryRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex); + sTarget.nBestSecondaryValue = sTarget.nValue; + sTarget.nSecondaryIndex = nIndex; + } + } + // Has less value or equal value and is closer. + else if(sTarget.nValue < sTarget.nBestValue || + (sTarget.nBestValue == sTarget.nValue && + GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange)) + { + sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex); + sTarget.nBestValue = sTarget.nValue; + sTarget.nIndex = nIndex; + } + return sTarget; +} +struct stTarget ai_CheckForHighestValuePhysicalTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1025", "Getting highest value physical index: " + sIndex + + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + + " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) + + " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2) + + " sTarget.nValue: " + IntToString(sTarget.nValue) + + " sTarget.nBestValue: " + IntToString(sTarget.nBestValue) + + " sTarget.nBestSecondaryValue: " + IntToString(sTarget.nBestSecondaryValue) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); + // Lets put any disabled targets and associates if set in a secondary group. + if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) || + !GetIsWeaponEffective(sTarget.oTarget) || + (ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && GetAssociateType(sTarget.oTarget))) + { + if(sTarget.nValue > sTarget.nBestSecondaryValue || + (sTarget.nValue == sTarget.nBestSecondaryValue && + GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestSecondaryRange)) + { + sTarget.fNearestSecondaryRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex); + sTarget.nBestSecondaryValue = sTarget.nValue; + sTarget.nSecondaryIndex = nIndex; + } + } + // Has less value or equal value and is closer. + else if(sTarget.nValue > sTarget.nBestValue || + (sTarget.nBestValue == sTarget.nValue && + GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex) < sTarget.fNearestRange)) + { + sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex); + sTarget.nBestValue = sTarget.nValue; + sTarget.nIndex = nIndex; + } + return sTarget; +} //****************************************************************************** //************ GET INDEX/TARGETs USING COMBAT STATE FUNCTIONS ****************** //****************************************************************************** // These functions will find a target based on the combat state variables created // by the function ai_SetCombatState for associates. -int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) { - return ai_GetLowestCRIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk); + return ai_GetLowestCRIndex(oCreature, fMaxRange, sTargetType); } struct stTarget sTarget; sTarget.fNearestRange = fMaxRange + 1.0; @@ -974,16 +1213,14 @@ int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "911", "Getting the nearest index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1211", "Getting the nearest index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(AI_DEBUG) ai_Debug("0i_combat", "918", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); - if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && - ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter); @@ -994,16 +1231,16 @@ int ai_GetNearestIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "931", "Found nearest [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1229", "Found nearest [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } -object ai_GetNearestTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +object ai_GetNearestTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { - if(AI_DEBUG) ai_Debug("0i_combat", "936", "Getting the nearest target."); - string sIndex = IntToString(ai_GetNearestIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "1234", "Getting the nearest target."); + string sIndex = IntToString(ai_GetNearestIndex(oCreature, fMaxRange, sTargetType)); return GetLocalObject(oCreature, sTargetType + sIndex); } -int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { struct stTarget sTarget; sTarget.fNearestRange = fMaxRange + 1.0; @@ -1016,16 +1253,14 @@ int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "953", "Getting the lowest CR index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1251", "Getting the lowest CR index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(AI_DEBUG) ai_Debug("0i_combat", "960", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); - if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && - ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); @@ -1037,16 +1272,16 @@ int ai_GetLowestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "974", "Found lowest CR [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1270", "Found lowest CR [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } -object ai_GetLowestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +object ai_GetLowestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { - if(AI_DEBUG) ai_Debug("0i_combat", "979", "Getting the lowest CR target."); - string sIndex = IntToString(ai_GetLowestCRIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "1275", "Getting the lowest CR target."); + string sIndex = IntToString(ai_GetLowestCRIndex(oCreature, fMaxRange, sTargetType)); return GetLocalObject(oCreature, sTargetType + sIndex); } -int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { struct stTarget sTarget; sTarget.fNearestRange = fMaxRange + 1.0; @@ -1059,16 +1294,14 @@ int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "995", "Getting the highest CR index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1292", "Getting the highest CR index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1002", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); - if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && - ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); @@ -1080,16 +1313,58 @@ int ai_GetHighestCRIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1016", "Found highest CR [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1311", "Found highest CR [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } -object ai_GetHighestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +object ai_GetHighestCRTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { - if(AI_DEBUG) ai_Debug("0i_combat", "1021", "Getting the highest CR target."); - string sIndex = IntToString(ai_GetHighestCRIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "1316", "Getting the highest CR target."); + string sIndex = IntToString(ai_GetHighestCRIndex(oCreature, fMaxRange, sTargetType)); return GetLocalObject(oCreature, sTargetType + sIndex); } -int ai_GetLowestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +int ai_GetNearestIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) + { + ai_GetLowestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType); + } + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.sTargetType = AI_ENEMY; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1335", "Getting the nearest not in AOE index: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && + !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) + { + if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1353", "Found nearest not in AOE Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetNearestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1358", "Getting the nearest not in AOE target."); + string sIndex = IntToString(ai_GetNearestIndexNotInAOE(oCreature, fMaxRange, sTargetType)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetLowestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { struct stTarget sTarget; sTarget.fNearestRange = fMaxRange + 1.0; @@ -1102,7 +1377,355 @@ int ai_GetLowestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTI sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1037", "Getting the lowest InMelee index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1375", "Getting the lowest CR not in AOE index: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) + { + if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); + sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1394", "Found lowest CR not in AOE [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetLowestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1399", "Getting the lowest cr not in AOE target."); + string sIndex = IntToString(ai_GetLowestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetHighestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = -100; + sTarget.nBestSecondaryValue = -100; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1416", "Getting the highest CR not in AOE index: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) + { + if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); + sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1435", "Found highest CR not in AOE [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetHighestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1440", "Getting the highest cr not in AOE target."); + string sIndex = IntToString(ai_GetHighestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetNearestPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) + { + return ai_GetLowestCRPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk); + } + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1464", "Getting the nearest physical index: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1471", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget = ai_CheckForNearestPhysicalTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1484", "Found nearest [" + sTargetType + "] Physical Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetNearestPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1484", "Getting the nearest physcial target."); + string sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetLowestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = 100; + sTarget.nBestSecondaryValue = 100; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1501", "Getting the lowest CR physical index: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1508", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); + sTarget = ai_CheckForLowestValuePhysicalTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1522", "Found lowest CR [" + sTargetType + "] Physical Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetLowestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1527", "Getting the lowest CR physical target."); + string sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetHighestCRPhysicalIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = -100; + sTarget.nBestSecondaryValue = -100; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1544", "Getting the highest CR physical index: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1551", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); + sTarget = ai_CheckForHighestValuePhysicalTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1565", "Found highest CR [" + sTargetType + "] Physical Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetHighestCRPhysicalTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1570", "Getting the highest CR physical target."); + string sIndex = IntToString(ai_GetHighestCRPhysicalIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetNearestPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) + { + ai_GetLowestCRPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk); + } + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.sTargetType = AI_ENEMY; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1589", "Getting the nearest physical index not in AOE: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && + !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1596", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && + ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1609", "Found nearest Physcial Index not in AOE: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetNearestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1614", "Getting the nearest physical target not in AOE."); + string sIndex = IntToString(ai_GetNearestPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetLowestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = 100; + sTarget.nBestSecondaryValue = 100; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1631", "Getting the lowest CR physical index not in AOE: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1551", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); + sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1652", "Found lowest CR Physical Index not in AOE [" + sTargetType + "]: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetLowestPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1657", "Getting the lowest cr physcial target not in AOE."); + string sIndex = IntToString(ai_GetLowestCRPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetHighestCRPhysicalIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = -100; + sTarget.nBestSecondaryValue = -100; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1674", "Getting the highest CR physical index not in AOE: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1681", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); + sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1695", "Found highest CR Physical Index not in AOE [" + sTargetType + "]: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetHighestCRPhysicalTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1700", "Getting the highest cr physical target not in AOE."); + string sIndex = IntToString(ai_GetHighestCRPhysicalIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetLowestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = 100; + sTarget.nBestSecondaryValue = 100; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1717", "Getting the lowest group index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); @@ -1121,10 +1744,10 @@ int ai_GetLowestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTI } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1058", "Found lowest InMelee [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1736", "Found lowest Group Index [" + sTargetType + "]: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } -int ai_GetHighestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +int ai_GetHighestGroupIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { struct stTarget sTarget; sTarget.fNearestRange = fMaxRange + 1.0; @@ -1137,7 +1760,7 @@ int ai_GetHighestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPT sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1073", "Getting the highest InMelee index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1752", "Getting the highest group index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); @@ -1156,13 +1779,54 @@ int ai_GetHighestMeleeIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPT } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1094", "Found highest InMelee [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1771", "Found highest Group Index [" + sTargetType + "]: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } -object ai_CheckForGroupedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +object ai_GetGroupTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) { - if(AI_DEBUG) ai_Debug("0i_combat", "1124", "Getting the highest InMelee target."); - string sIndex = IntToString(ai_GetHighestMeleeIndex(oCreature, fMaxRange, sTargetType)); + if(AI_DEBUG) ai_Debug("0i_combat", "1776", "Getting the highest group target."); + string sIndex = IntToString(ai_GetHighestGroupIndex(oCreature, fMaxRange, sTargetType)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetHighestGroupIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = -100; + sTarget.nBestSecondaryValue = -100; + sTarget.sTargetType = sTargetType; + int nCounter = 1; + string sCounter = "1"; + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + while(sTarget.oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1793", "Getting the highest group index not in AOE: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && + !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) + { + if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_MELEE" + sCounter); + sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); + } + // If we do not have a normal target then use our best secondary target. + if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; + if(AI_DEBUG) ai_Debug("0i_combat", "1812", "Found highest Group Index not in AOE [" + sTargetType + "]: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_GetGroupTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1818", "Getting the highest group target not in AOE."); + string sIndex = IntToString(ai_GetHighestGroupIndexNotInAOE(oCreature, fMaxRange, sTargetType)); return GetLocalObject(oCreature, sTargetType + sIndex); } int ai_GetMostWoundedIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) @@ -1178,14 +1842,14 @@ int ai_GetMostWoundedIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTI sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1113", "Getting the most wounded index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1834", "Getting the most wounded index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1120", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "1841", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) @@ -1199,12 +1863,12 @@ int ai_GetMostWoundedIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTI } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1130", "Found most wounded [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1855", "Found most wounded [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } object ai_GetMostWoundedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) { - if(AI_DEBUG) ai_Debug("0i_combat", "1139", "Getting the most wounded target."); + if(AI_DEBUG) ai_Debug("0i_combat", "1860", "Getting the most wounded target."); string sIndex = IntToString(ai_GetMostWoundedIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); return GetLocalObject(oCreature, sTargetType + sIndex); } @@ -1219,7 +1883,7 @@ int ai_GetAllyToHealIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTIO sTarget.oTarget = GetLocalObject(oCreature, sTarget.sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1154", "Getting the most wounded ally to heal index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1875", "Getting the most wounded ally to heal index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ALLY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); @@ -1237,12 +1901,12 @@ int ai_GetAllyToHealIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTIO sTarget.oTarget = GetLocalObject(oCreature, AI_ALLY + sCounter); } // If we do not have a normal target then we are done.. - if(AI_DEBUG) ai_Debug("0i_combat", "1187", "Found most wounded ally to heal Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1893", "Found most wounded ally to heal Index: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } object ai_GetAllyToHealTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION) { - if(AI_DEBUG) ai_Debug("0i_combat", "1192", "Getting the most wounded ally to heal target."); + if(AI_DEBUG) ai_Debug("0i_combat", "1898", "Getting the most wounded ally to heal target."); string sIndex = IntToString(ai_GetAllyToHealIndex(oCreature, fMaxRange)); return GetLocalObject(oCreature, AI_ALLY + sIndex); } @@ -1259,14 +1923,14 @@ object ai_GetLowestFortitudeSaveTarget(object oCreature, float fMaxRange = AI_RA sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1113", "Getting the lowest fortitude save index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1915", "Getting the lowest fortitude save index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) + + if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { sTarget.nValue = GetFortitudeSavingThrow(sTarget.oTarget); @@ -1278,7 +1942,7 @@ object ai_GetLowestFortitudeSaveTarget(object oCreature, float fMaxRange = AI_RA } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1232", "Found lowest fortitude save Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1934", "Found lowest fortitude save Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex)); } object ai_GetLowestReflexSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION) @@ -1294,14 +1958,14 @@ object ai_GetLowestReflexSaveTarget(object oCreature, float fMaxRange = AI_RANGE sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1248", "Getting the lowest reflex save index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1950", "Getting the lowest reflex save index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) + + if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { sTarget.nValue = GetReflexSavingThrow(sTarget.oTarget); @@ -1313,7 +1977,7 @@ object ai_GetLowestReflexSaveTarget(object oCreature, float fMaxRange = AI_RANGE } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1269", "Found lowest reflex save Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "1969", "Found lowest reflex save Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex)); } object ai_GetLowestWillSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION) @@ -1329,14 +1993,14 @@ object ai_GetLowestWillSaveTarget(object oCreature, float fMaxRange = AI_RANGE_P sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1285", "Getting the lowest will save index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "1985", "Getting the lowest will save index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) + + if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { sTarget.nValue = GetWillSavingThrow(sTarget.oTarget); @@ -1348,7 +2012,7 @@ object ai_GetLowestWillSaveTarget(object oCreature, float fMaxRange = AI_RANGE_P } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1306", "Found lowest will save Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2004", "Found lowest will save Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex)); } object ai_GetSpellTargetBasedOnSaves(object oCreature, int nSpell, float fMaxRange = AI_RANGE_PERCEPTION) @@ -1373,17 +2037,17 @@ int ai_GetNearestIndexThatSeesUs(object oCreature, float fMaxRange = AI_RANGE_PE sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1334", "Getting the nearest creature that sees us index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2029", "Getting the nearest creature that sees us index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) + + if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1373", GetName(sTarget.oTarget) + " can see us? " + + if(AI_DEBUG) ai_Debug("0i_combat", "2039", GetName(sTarget.oTarget) + " can see us? " + IntToString(GetObjectSeen(oCreature, sTarget.oTarget))); if(GetObjectSeen(oCreature, sTarget.oTarget)) { @@ -1395,7 +2059,7 @@ int ai_GetNearestIndexThatSeesUs(object oCreature, float fMaxRange = AI_RANGE_PE sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); } // If we do not have a normal target then use our best secondary target. - if(AI_DEBUG) ai_Debug("0i_combat", "1354", "Found nearest creature that sees us Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2051", "Found nearest creature that sees us Index: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } int ai_GetBestSneakAttackIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE) @@ -1410,7 +2074,7 @@ int ai_GetBestSneakAttackIndex(object oCreature, float fMaxRange = AI_RANGE_PERC sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1372", "Getting the best sneak attack index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2066", "Getting the best sneak attack index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); @@ -1418,11 +2082,15 @@ int ai_GetBestSneakAttackIndex(object oCreature, float fMaxRange = AI_RANGE_PERC !GetIsDead(sTarget.oTarget) && !ai_IsImmuneToSneakAttacks(oCreature, sTarget.oTarget)) { - if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) + + if(AI_DEBUG) ai_Debug("0i_combat", "2074", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && + ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { oAttacking = ai_GetAttackedTarget(sTarget.oTarget); - if(AI_DEBUG) ai_Debug("0i_combat", "1383", "oTarget: " + GetName(sTarget.oTarget) + + if(AI_DEBUG) ai_Debug("0i_combat", "2082", "oTarget: " + GetName(sTarget.oTarget) + " is attacking " + GetName(oAttacking)); // They are attacking someone besides us or we are hidden? if((oAttacking != OBJECT_INVALID && oAttacking != oCreature) || @@ -1437,178 +2105,9 @@ int ai_GetBestSneakAttackIndex(object oCreature, float fMaxRange = AI_RANGE_PERC } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1398", "Found best sneak attack Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2097", "Found best sneak attack Index: " + IntToString(sTarget.nIndex)); return sTarget.nIndex; } -int ai_GetNearestIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) -{ - if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) - { - ai_GetLowestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk); - } - struct stTarget sTarget; - sTarget.fNearestRange = fMaxRange + 1.0; - sTarget.fNearestSecondaryRange = sTarget.fNearestRange; - sTarget.sTargetType = AI_ENEMY; - int nCounter = 1; - string sCounter = "1"; - sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); - while(sTarget.oTarget != OBJECT_INVALID) - { - if(AI_DEBUG) ai_Debug("0i_combat", "1416", "Getting the nearest not in AOE index: " + - sCounter + " " + GetName(sTarget.oTarget) + - " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + - " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); - if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && - !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) - { - if(ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) + - ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) - { - sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter); - } - } - sCounter = IntToString(++nCounter); - sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); - } - // If we do not have a normal target then use our best secondary target. - if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1434", "Found nearest not in AOE Index: " + IntToString(sTarget.nIndex)); - return sTarget.nIndex; -} -object ai_GetNearestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) -{ - if(AI_DEBUG) ai_Debug("0i_combat", "1439", "Getting the nearest not in AOE target."); - string sIndex = IntToString(ai_GetNearestIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); - return GetLocalObject(oCreature, sTargetType + sIndex); -} -int ai_GetLowestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) -{ - struct stTarget sTarget; - sTarget.fNearestRange = fMaxRange + 1.0; - sTarget.fNearestSecondaryRange = sTarget.fNearestRange; - sTarget.nBestValue = 100; - sTarget.nBestSecondaryValue = 100; - sTarget.sTargetType = sTargetType; - int nCounter = 1; - string sCounter = "1"; - sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); - while(sTarget.oTarget != OBJECT_INVALID) - { - if(AI_DEBUG) ai_Debug("0i_combat", "1456", "Getting the lowest CR not in AOE index: " + - sCounter + " " + GetName(sTarget.oTarget) + - " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + - " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); - if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && - !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) - { - if(AI_DEBUG) ai_Debug("0i_combat", "1463", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); - if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && - ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && - ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) - { - sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); - sTarget = ai_CheckForLowestValueTarget(oCreature, sTarget, nCounter, sCounter); - } - } - sCounter = IntToString(++nCounter); - sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); - } - // If we do not have a normal target then use our best secondary target. - if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1477", "Found lowest CR not in AOE [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); - return sTarget.nIndex; -} -object ai_GetLowestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) -{ - if(AI_DEBUG) ai_Debug("0i_combat", "1482", "Getting the lowest cr not in AOE target."); - string sIndex = IntToString(ai_GetLowestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); - return GetLocalObject(oCreature, sTargetType + sIndex); -} -int ai_GetHighestCRIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) -{ - struct stTarget sTarget; - sTarget.fNearestRange = fMaxRange + 1.0; - sTarget.fNearestSecondaryRange = sTarget.fNearestRange; - sTarget.nBestValue = -100; - sTarget.nBestSecondaryValue = -100; - sTarget.sTargetType = sTargetType; - int nCounter = 1; - string sCounter = "1"; - sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); - while(sTarget.oTarget != OBJECT_INVALID) - { - if(AI_DEBUG) ai_Debug("0i_combat", "1499", "Getting the highest CR not in AOE index: " + - sCounter + " " + GetName(sTarget.oTarget) + - " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + - " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); - if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && - !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) - { - if(AI_DEBUG) ai_Debug("0i_combat", "1506", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); - if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && - ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && - ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) - { - sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_COMBAT" + sCounter); - sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter); - } - } - sCounter = IntToString(++nCounter); - sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); - } - // If we do not have a normal target then use our best secondary target. - if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1520", "Found highest CR not in AOE [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); - return sTarget.nIndex; -} -object ai_GetHighestTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) -{ - if(AI_DEBUG) ai_Debug("0i_combat", "1525", "Getting the highest cr not in AOE target."); - string sIndex = IntToString(ai_GetHighestCRIndexNotInAOE(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); - return GetLocalObject(oCreature, sTargetType + sIndex); -} -int ai_GetHighestMeleeIndexNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) -{ - struct stTarget sTarget; - sTarget.fNearestRange = fMaxRange + 1.0; - sTarget.fNearestSecondaryRange = sTarget.fNearestRange; - sTarget.nBestValue = -100; - sTarget.nBestSecondaryValue = -100; - sTarget.sTargetType = sTargetType; - int nCounter = 1; - string sCounter = "1"; - sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); - while(sTarget.oTarget != OBJECT_INVALID) - { - if(AI_DEBUG) ai_Debug("0i_combat", "1542", "Getting the highest InMelee not in AOE index: " + - sCounter + " " + GetName(sTarget.oTarget) + - " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + - " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); - if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && - !GetIsDead(sTarget.oTarget) && !ai_IsInADangerousAOE(sTarget.oTarget)) - { - if(ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && - ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) - { - sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_MELEE" + sCounter); - sTarget = ai_CheckForHighestValueTarget(oCreature, sTarget, nCounter, sCounter); - } - } - sCounter = IntToString(++nCounter); - sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); - } - // If we do not have a normal target then use our best secondary target. - if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1563", "Found highest InMelee not in AOE [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); - return sTarget.nIndex; -} -object ai_CheckForGroupedTargetNotInAOE(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY) -{ - if(AI_DEBUG) ai_Debug("0i_combat", "1574", "Getting the highest InMelee not in AOE target."); - string sIndex = IntToString(ai_GetHighestMeleeIndexNotInAOE(oCreature, fMaxRange, sTargetType)); - return GetLocalObject(oCreature, sTargetType + sIndex); -} object ai_GetNearestClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) { if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) @@ -1624,15 +2123,17 @@ object ai_GetNearestClassTarget(object oCreature, int nClassType, float fMaxRang sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1591", "Getting the nearest class index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2115", "Getting the nearest class index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget) && ai_CheckClassType(sTarget.oTarget, nClassType)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1598", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2122", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { @@ -1644,7 +2145,7 @@ object ai_GetNearestClassTarget(object oCreature, int nClassType, float fMaxRang } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1611", "Found nearest class Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2137", "Found nearest class Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex)); } object ai_GetLowestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) @@ -1660,15 +2161,17 @@ object ai_GetLowestCRClassTarget(object oCreature, int nClassType, float fMaxRan sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1626", "Getting the lowest CR class index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2153", "Getting the lowest CR class index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget) && ai_CheckClassType(sTarget.oTarget, nClassType)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1634", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2160", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { @@ -1681,7 +2184,7 @@ object ai_GetLowestCRClassTarget(object oCreature, int nClassType, float fMaxRan } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1648", "Found lowest CR class [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2176", "Found lowest CR class [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex)); } object ai_GetHighestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) @@ -1697,15 +2200,17 @@ object ai_GetHighestCRClassTarget(object oCreature, int nClassType, float fMaxRa sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1664", "Getting the highest CR class index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2192", "Getting the highest CR class index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget) && ai_CheckClassType(sTarget.oTarget, nClassType)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1671", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2199", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { @@ -1718,7 +2223,7 @@ object ai_GetHighestCRClassTarget(object oCreature, int nClassType, float fMaxRa } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1685", "Found highest CR class [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2215", "Found highest CR class [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex)); } object ai_GetNearestRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) @@ -1736,15 +2241,17 @@ object ai_GetNearestRacialTarget(object oCreature, int nRacialType, float fMaxRa sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1703", "Getting the nearest race index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2233", "Getting the nearest race index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1710", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2240", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { @@ -1756,7 +2263,7 @@ object ai_GetNearestRacialTarget(object oCreature, int nRacialType, float fMaxRa } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1723", "Found nearest race Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2255", "Found nearest race Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex)); } object ai_GetLowestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) @@ -1772,15 +2279,17 @@ object ai_GetLowestCRRacialTarget(object oCreature, int nRacialType, float fMaxR sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1739", "Getting the lowest CR race index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2271", "Getting the lowest CR race index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1746", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2278", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { @@ -1793,7 +2302,7 @@ object ai_GetLowestCRRacialTarget(object oCreature, int nRacialType, float fMaxR } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1760", "Found lowest CR race [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2294", "Found lowest CR race [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex)); } object ai_GetHighestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE) @@ -1809,15 +2318,17 @@ object ai_GetHighestCRRacialTarget(object oCreature, int nRacialType, float fMax sTarget.oTarget = GetLocalObject(oCreature, sTargetType + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1776", "Getting the highest CR race index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2310", "Getting the highest CR race index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, sTargetType + "_PERCEIVED" + sCounter) && !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1783", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2317", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { @@ -1830,7 +2341,7 @@ object ai_GetHighestCRRacialTarget(object oCreature, int nRacialType, float fMax } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1797", "Found highest CR race [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2333", "Found highest CR race [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, sTargetType + IntToString(sTarget.nIndex)); } object ai_GetNearestFavoredEnemyTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE) @@ -1973,16 +2484,18 @@ object ai_GetNearestFavoredEnemyTarget(object oCreature, float fMaxRange = AI_RA sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "1940", "Getting the nearest favored race index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2476", "Getting the nearest favored race index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && !GetIsDead(sTarget.oTarget) && ai_CheckRacialType(sTarget.oTarget, nRacialType)) { - if(AI_DEBUG) ai_Debug("0i_combat", "1947", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2483", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && - ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) + + GetIsWeaponEffective(sTarget.oTarget) && + ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { sTarget = ai_CheckForNearestTarget(oCreature, sTarget, nCounter, sCounter); @@ -1995,7 +2508,7 @@ object ai_GetNearestFavoredEnemyTarget(object oCreature, float fMaxRange = AI_RA } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "1962", "Found nearest favored race Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2500", "Found nearest favored race Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex)); } object ai_GetFlankTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE) @@ -2007,17 +2520,23 @@ object ai_GetFlankTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION while(oAlly != OBJECT_INVALID) { fAllyRange = GetLocalFloat(oCreature, AI_ALLY_RANGE + sCnt); - if(AI_DEBUG) ai_Debug("0i_combat", "1974", "Getting Ally being Flanked Index: " + sCnt + " " + + if(AI_DEBUG) ai_Debug("0i_combat", "2512", "Getting Ally being Flanked Index: " + sCnt + " " + GetName(oAlly) + " fAllyRange: " + FloatToString(fAllyRange, 0, 2) + " fMaxRange: " + FloatToString(fMaxRange, 0, 2)); if(fAllyRange <= fMaxRange) { nInMelee = GetLocalInt(oCreature, AI_ALLY_MELEE + sCnt); - if(AI_DEBUG) ai_Debug("0i_combat", "1980", "nInMelee: " + IntToString(nInMelee)); + if(AI_DEBUG) ai_Debug("0i_combat", "2519", "nInMelee: " + IntToString(nInMelee)); if(!GetIsDead(oAlly) && nInMelee > nHighestMelee) { - oTarget = ai_GetEnemyAttackingMyAlly(oCreature, oAlly, fMaxRange); - if(oTarget != OBJECT_INVALID) nHighestMelee = nInMelee; + if(AI_DEBUG) ai_Debug("0i_combat", "2521", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(oTarget))); + if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCnt)) && + GetIsWeaponEffective(oTarget)) + { + oTarget = ai_GetEnemyAttackingMyAlly(oCreature, oAlly, fMaxRange); + if(oTarget != OBJECT_INVALID) nHighestMelee = nInMelee; + } } } sCnt = IntToString(++nCnt); @@ -2028,12 +2547,12 @@ object ai_GetFlankTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION { // If we just checked within melee then lets check what we can see if // we can move around in combat. - if (fMaxRange == AI_RANGE_MELEE && ai_CanIMoveInCombat(oCreature)) + if(fMaxRange == AI_RANGE_MELEE && ai_CanIMoveInCombat(oCreature)) { oTarget = ai_GetFlankTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk); } } - if(AI_DEBUG) ai_Debug("0i_combat", "2000", "oTarget " + GetName(oTarget) + + if(AI_DEBUG) ai_Debug("0i_combat", "2544", "oTarget " + GetName(oTarget) + " is attacking " + GetName(oAlly)); return oTarget; } @@ -2048,15 +2567,17 @@ object ai_GetRangedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTIO sTarget.oTarget = GetLocalObject(oCreature, AI_ENEMY + sCounter); while(sTarget.oTarget != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "2037", "Getting the nearest ranged index: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2559", "Getting the nearest ranged index: " + sCounter + " " + GetName(sTarget.oTarget) + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter)) + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); if(GetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCounter) && !GetIsDead(sTarget.oTarget)) { - if(AI_DEBUG) ai_Debug("0i_combat", "2044", "bAlwaysAtk: " + IntToString(bAlwaysAtk)); + if(AI_DEBUG) ai_Debug("0i_combat", "2566", "bAlwaysAtk: " + IntToString(bAlwaysAtk) + + " Weapon Effective: " + IntToString(GetIsWeaponEffective(sTarget.oTarget))); if((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + GetIsWeaponEffective(sTarget.oTarget) && ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) { @@ -2071,7 +2592,7 @@ object ai_GetRangedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTIO } // If we do not have a normal target then use our best secondary target. if(sTarget.nIndex == 0 && sTarget.nSecondaryIndex != 0) sTarget.nIndex = sTarget.nSecondaryIndex; - if(AI_DEBUG) ai_Debug("0i_combat", "2060", "Found nearest ranged Index: " + IntToString(sTarget.nIndex)); + if(AI_DEBUG) ai_Debug("0i_combat", "2584", "Found nearest ranged Index: " + IntToString(sTarget.nIndex)); return GetLocalObject(oCreature, AI_ENEMY + IntToString(sTarget.nIndex)); } object ai_GetBestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) @@ -2084,25 +2605,24 @@ object ai_GetBestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlway { if(ai_CanIMoveInCombat(oCreature)) { - sIndex = IntToString(ai_GetLowestCRIndex(oCreature, AI_RANGE_MELEE)); + sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_MELEE)); } - else sIndex = IntToString(ai_GetNearestIndex(oCreature, AI_RANGE_MELEE)); + else sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, AI_RANGE_MELEE)); } // If not then lets go find someone to attack! else { // If we are not in melee then we should get the nearest enemy. - sIndex = IntToString(ai_GetNearestIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); - /* Lets stay out of bad AOE's. + sIndex = IntToString(ai_GetNearestPhysicalIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); // If we didn't get a target then get any target within range. if(sIndex == "0") { - sIndex = IntToString(ai_GetLowestCRIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); - } */ + sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); + } } object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); // We might not have a target this is fine as sometimes we don't want to attack! - if(AI_DEBUG) ai_Debug("0i_combat", "2048", GetName(oTarget) + " is the best target for melee combat!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2614", GetName(oTarget) + " is the best target for melee combat!"); return oTarget; } object ai_GetNearestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) @@ -2111,21 +2631,23 @@ object ai_GetNearestTargetForMeleeCombat(object oCreature, int nInMelee, int bAl if(oPCTarget != OBJECT_INVALID) return oPCTarget; string sIndex; // Are we in melee? If so try to get the nearest enemy in melee. - if(nInMelee > 0) sIndex = IntToString(ai_GetNearestIndex(oCreature, AI_RANGE_MELEE)); + if(nInMelee > 0) sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, AI_RANGE_MELEE)); // If not then lets go find someone to attack! else { // Get the nearest enemy. - sIndex = IntToString(ai_GetNearestIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); + sIndex = IntToString(ai_GetNearestPhysicalIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); // If we didn't get a target then get any target within range. if(sIndex == "0") { - sIndex = IntToString(ai_GetNearestIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); + sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); } } object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); + // If we don't have a target lets try one last time and see if we can find one. + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); // We might not have a target this is fine as sometimes we don't want to attack! - if(AI_DEBUG) ai_Debug("0i_combat", "2024", GetName(oTarget) + " is the nearest target for melee combat!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2637", GetName(oTarget) + " is the nearest target for melee combat!"); return oTarget; } object ai_GetLowestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) @@ -2134,22 +2656,23 @@ object ai_GetLowestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bA if(oPCTarget != OBJECT_INVALID) return oPCTarget; string sIndex; // Are we in melee? If so try to get the weakest enemy in melee. - if(nInMelee > 0) sIndex = IntToString(ai_GetLowestCRIndex(oCreature, AI_RANGE_MELEE)); + if(nInMelee > 0) sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_MELEE)); // If not then lets go find someone to attack! else { // Get the weakest combat rated enemy. - sIndex = IntToString(ai_GetLowestCRIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); - /* Lets stay out of bad AOE's. + sIndex = IntToString(ai_GetLowestCRPhysicalIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); // If we didn't get a target then get any target within range. if(sIndex == "0") { - sIndex = IntToString(ai_GetLowestCRIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); - } */ + sIndex = IntToString(ai_GetLowestCRPhysicalIndex(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk)); + } } object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); + // If we don't have a target lets try one last time and see if we can find one. + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); // We might not have a target this is fine as sometimes we don't want to attack! - if(AI_DEBUG) ai_Debug("0i_combat", "2048", GetName(oTarget) + " is the weakest target for melee combat!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2660", GetName(oTarget) + " is the weakest target for melee combat!"); return oTarget; } object ai_GetHighestCRTargetForMeleeCombat(object oCreature, int nInMelee) @@ -2158,20 +2681,20 @@ object ai_GetHighestCRTargetForMeleeCombat(object oCreature, int nInMelee) if(oPCTarget != OBJECT_INVALID) return oPCTarget; string sIndex; // Are we in melee? If so try to get the weakest enemy in melee. - if(nInMelee > 0) sIndex = IntToString(ai_GetHighestCRIndex(oCreature, AI_RANGE_MELEE)); + if(nInMelee > 0) sIndex = IntToString(ai_GetHighestCRPhysicalIndex(oCreature, AI_RANGE_MELEE)); // If not then lets go find someone to attack! else { // Get the weakest combat rated enemy. - sIndex = IntToString(ai_GetHighestCRIndexNotInAOE(oCreature, AI_RANGE_PERCEPTION)); - /* Lets stay out of bad AOE's. + sIndex = IntToString(ai_GetHighestCRPhysicalIndexNotInAOE(oCreature)); // If we didn't get a target then get any target within range. - if(sIndex == "0") sIndex = IntToString(ai_GetHighestCRIndex(oCreature)); - */ + if(sIndex == "0") sIndex = IntToString(ai_GetHighestCRPhysicalIndex(oCreature)); } object oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); + // If we don't have a target lets try one last time and see if we can find one. + if(oTarget == OBJECT_INVALID) oTarget = ai_GetHighestCRPhysicalTarget(oCreature); // We might not have a target this is fine as sometimes we don't want to attack! - if(AI_DEBUG) ai_Debug("0i_combat", "2070", GetName(oTarget) + " is the strongest target for melee combat!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2680", GetName(oTarget) + " is the strongest target for melee combat!"); return oTarget; } object ai_GetEnemyAttackingMe(object oCreature, float fMaxRange = AI_RANGE_MELEE) @@ -2186,7 +2709,7 @@ object ai_GetEnemyAttackingMe(object oCreature, float fMaxRange = AI_RANGE_MELEE if(!ai_Disabled(oEnemy)) { fDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sCtr); - if(AI_DEBUG) ai_Debug("0i_combat", "2084", "Getting Enemy Attacking Me: " + sCtr + " " + + if(AI_DEBUG) ai_Debug("0i_combat", "2695", "Getting Enemy Attacking Me: " + sCtr + " " + GetName(oEnemy) + " fTargetRange: " + FloatToString(fDistance, 0, 2) + " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " Attacking: " + GetName(ai_GetAttackedTarget(oEnemy))); @@ -2196,7 +2719,7 @@ object ai_GetEnemyAttackingMe(object oCreature, float fMaxRange = AI_RANGE_MELEE // If an enemy isn't attacking someone we must assume we are next! if(oAttacked == oCreature || oAttacked == OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "2095", "Enemy attacking me: " + GetName(oEnemy) + " has attacked: " + GetName(ai_GetAttackedTarget(oEnemy))); + if(AI_DEBUG) ai_Debug("0i_combat", "2705", "Enemy attacking me: " + GetName(oEnemy) + " has attacked: " + GetName(ai_GetAttackedTarget(oEnemy))); return oEnemy; } } @@ -2218,7 +2741,7 @@ object ai_GetEnemyAttackingMyAlly(object oCreature, object oAlly, float fMaxRang while(oEnemy != OBJECT_INVALID) { fEnemyRange = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sCtr); - if(AI_DEBUG) ai_Debug("0i_combat", "2117", "Getting Enemy Attacking Ally:" + + if(AI_DEBUG) ai_Debug("0i_combat", "2727", "Getting Enemy Attacking Ally:" + GetName(oAlly) + ": " + sCtr + " InMelee:" + GetName(oEnemy) + " fEnemyRange: " + FloatToString(fEnemyRange, 0, 2) + " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " Attacking: " + @@ -2226,7 +2749,7 @@ object ai_GetEnemyAttackingMyAlly(object oCreature, object oAlly, float fMaxRang if(fEnemyRange <= fMaxRange) { oAttacked = ai_GetAttackedTarget(oEnemy); - if(AI_DEBUG) ai_Debug("0i_combat", "2125", "Enemy attacking " + + if(AI_DEBUG) ai_Debug("0i_combat", "2735", "Enemy attacking " + GetName(oAlly) + ": " + GetName(oEnemy) + " has attacked: " + GetName(ai_GetAttackedTarget(oEnemy))); // If an enemy isn't attacking someone we must assume we are next! @@ -2271,7 +2794,7 @@ int ai_GetNumOfEnemiesInRange(object oCreature, float fMaxRange = AI_RANGE_MELEE if(fDistance < fMaxRange) nNumOfEnemies ++; fDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + IntToString(++nCnt)); } - if(AI_DEBUG) ai_Debug("0i_combat", "2459", IntToString (nNumOfEnemies) + " enemies within " + FloatToString(fMaxRange, 0, 2) + " meters."); + if(AI_DEBUG) ai_Debug("0i_combat", "2780", IntToString (nNumOfEnemies) + " enemies within " + FloatToString(fMaxRange, 0, 2) + " meters."); return nNumOfEnemies; } object ai_GetAllyBuffTarget(object oCreature, int nSpell, float fMaxRange = AI_RANGE_BATTLEFIELD) @@ -2284,7 +2807,7 @@ object ai_GetAllyBuffTarget(object oCreature, int nSpell, float fMaxRange = AI_R if(fMaxRange > fNearestEnemy) fMaxRange = fNearestEnemy; // Now lets get the best target based on the spell data in ai_spells.2da string sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell); - if(AI_DEBUG) ai_Debug("0i_combat", "2596", "sBuffTarget: " + sBuffTarget + " fMaxRange: " + FloatToString(fMaxRange, 0, 2)); + if(AI_DEBUG) ai_Debug("0i_combat", "2793", "sBuffTarget: " + sBuffTarget + " fMaxRange: " + FloatToString(fMaxRange, 0, 2)); if(sBuffTarget == "0") return oCreature; if(sBuffTarget == "1") return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_STRENGTH, "", fMaxRange, AI_ALLY); @@ -2325,7 +2848,7 @@ int ai_GetCurrentRound(object oCreature) { int nRound = GetLocalInt(oCreature, AI_ROUND) + 1; SetLocalInt(oCreature, AI_ROUND, nRound); - if(AI_DEBUG) ai_Debug("0i_combat", "2471", "nRound: " + IntToString(nRound)); + if(AI_DEBUG) ai_Debug("0i_combat", "2834", "nRound: " + IntToString(nRound)); return nRound; } int ai_GetDifficulty(object oCreature) @@ -2333,7 +2856,7 @@ int ai_GetDifficulty(object oCreature) int nAdjustment = GetLocalInt(oCreature, AI_DIFFICULTY_ADJUSTMENT); int nDifficulty = GetLocalInt(oCreature, AI_ENEMY_POWER) - GetLocalInt(oCreature, AI_ALLY_POWER) + 13 + nAdjustment; if(nDifficulty < 1) nDifficulty = 1; - if(AI_DEBUG) ai_Debug("0i_combat", "2474", "(Difficulty: Enemy Power: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_POWER)) + + if(AI_DEBUG) ai_Debug("0i_combat", "2842", "(Difficulty: Enemy Power: " + IntToString(GetLocalInt(oCreature, AI_ENEMY_POWER)) + " - Ally Power: " + IntToString(GetLocalInt(oCreature, AI_ALLY_POWER)) + ") + 13 + nAdj: " + IntToString(nAdjustment) + " = " + IntToString(nDifficulty) + "(Min of 1)"); @@ -2349,7 +2872,7 @@ int ai_GetMyCombatRating(object oCreature) } else nAtkBonus += GetAbilityModifier(ABILITY_STRENGTH, oCreature); if(ai_GetIsMeleeWeapon(oWeapon)) nAtkBonus += ai_GetWeaponAtkBonus(oWeapon); - if(AI_DEBUG) ai_Debug("0i_combat", "2496", "GetMyCombatRating (nAtkBonus: " + IntToString(nAtkBonus) + + if(AI_DEBUG) ai_Debug("0i_combat", "2858", "GetMyCombatRating (nAtkBonus: " + IntToString(nAtkBonus) + " nAC: " + IntToString(GetAC(oCreature)) + " - 10) / 2 = " + IntToString((nAtkBonus + GetAC(oCreature) - 10) / 2)); return(nAtkBonus + GetAC(oCreature) - 10) / 2; @@ -2425,7 +2948,7 @@ void ai_SetNormalAppearance(object oCreature) if(!ai_GetHasEffectType(oCreature, EFFECT_TYPE_POLYMORPH)) { int nForm = GetAppearanceType(oCreature); - if(AI_DEBUG) ai_Debug("0i_combat", "2729", GetName(oCreature) + " form: " + IntToString(nForm)); + if(AI_DEBUG) ai_Debug("0i_combat", "2835", GetName(oCreature) + " form: " + IntToString(nForm)); SetLocalInt(oCreature, AI_NORMAL_FORM, nForm + 1); } } @@ -2500,7 +3023,7 @@ struct stClasses ai_GetFactionsClasses(object oCreature, int bEnemy = TRUE, floa if(bEnemy) oTarget = ai_GetNearestEnemy(oCreature, ++nCnt, 7, 7); else oTarget = ai_GetNearestAlly(oCreature, ++nCnt, 7, 7); } - if(AI_DEBUG) ai_Debug("0i_combat", "2627", "Enemy: " + IntToString(bEnemy) + " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + + if(AI_DEBUG) ai_Debug("0i_combat", "2910", "Enemy: " + IntToString(bEnemy) + " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " CLERICS: " + IntToString(sCount.CLERICS) + "(" + IntToString(sCount.CLERIC_LEVELS) + ") FIGHTERS: " +IntToString(sCount.FIGHTERS) + "(" + IntToString(sCount.FIGHTER_LEVELS) + ") MAGES: " +IntToString(sCount.MAGES) + "(" + IntToString(sCount.MAGE_LEVELS) + @@ -2542,11 +3065,11 @@ void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID) nRacialType == RACIAL_TYPE_OOZE || nRacialType == RACIAL_TYPE_VERMIN) return; //if(Polymorphed()) return; - if(AI_DEBUG) ai_Debug("0i_combat", "2669", GetName(OBJECT_SELF) + " is equiping best weapon!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2952", GetName(OBJECT_SELF) + " is equiping best weapon!"); // Determine if I am wielding a ranged weapon, melee weapon, or none. int bIsWieldingRanged = ai_HasRangedWeaponWithAmmo(oCreature); int bIsWieldingMelee = ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); - if(AI_DEBUG) ai_Debug("0i_combat", "2673", "bIsWieldingRanged: " + IntToString(bIsWieldingRanged) + + if(AI_DEBUG) ai_Debug("0i_combat", "2956", "bIsWieldingRanged: " + IntToString(bIsWieldingRanged) + " bIsWieldingMelee: " + IntToString(bIsWieldingMelee)); // If we are hidden then change to a melee weapon so we can move in to attack. if(ai_GetIsHidden(oCreature)) @@ -2557,7 +3080,7 @@ void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID) } // Equip the appropriate weapon for the distance of the enemy. int nEnemyGroup = ai_GetNumOfEnemiesInGroup(oCreature); - if(AI_DEBUG) ai_Debug("0i_combat", "2684", GetName(oCreature) + " has " + IntToString(nEnemyGroup) + " enemies within 5.0f them! PointBlank: " + + if(AI_DEBUG) ai_Debug("0i_combat", "2967", GetName(oCreature) + " has " + IntToString(nEnemyGroup) + " enemies within 5.0f them! PointBlank: " + IntToString(GetHasFeat(FEAT_POINT_BLANK_SHOT, oCreature))); // We are in melee combat. if(nEnemyGroup > 0) @@ -2572,7 +3095,7 @@ void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID) if(!bIsWieldingMelee) { ai_EquipBestMeleeWeapon(oCreature); - if(AI_DEBUG) ai_Debug("0i_combat", "2699", GetName(oCreature) + " is equiping melee weapon due to close enemies!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2982", GetName(oCreature) + " is equiping melee weapon due to close enemies!"); } } } @@ -2580,7 +3103,7 @@ void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID) // We are not in melee range. else { - if(AI_DEBUG) ai_Debug("0i_combat", "2707", GetName(oCreature) + " is not in melee combat with an enemy!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2990", GetName(oCreature) + " is not in melee combat with an enemy!"); // If are at range with the enemy then equip a ranged weapon. if(!bIsWieldingRanged) { @@ -2588,7 +3111,7 @@ void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID) // Make sure that they equiped a range weapon. bIsWieldingRanged = ai_HasRangedWeaponWithAmmo(oCreature); bIsWieldingMelee = ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature)); - if(AI_DEBUG) ai_Debug("0i_combat", "2719", GetName(oCreature) + " is attempting to equip a ranged weapon: " + IntToString(bIsWieldingRanged)); + if(AI_DEBUG) ai_Debug("0i_combat", "2998", GetName(oCreature) + " is attempting to equip a ranged weapon: " + IntToString(bIsWieldingRanged)); // If we equiped a ranged weapon then drop out. } } @@ -2598,12 +3121,12 @@ void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID) int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) { if(ai_GetAIMode(oCreature, AI_MODE_EQUIP_WEAPON_OFF)) return FALSE; - if(AI_DEBUG) ai_Debug("0i_combat", "3049", GetName(oCreature) + " is equiping best melee weapon!"); + if(AI_DEBUG) ai_Debug("0i_combat", "2992", GetName(oCreature) + " is equiping best melee weapon!"); float fItemPower, fOffItemPower, fRightPower, fLeftPower, f2HandedPower; int nItemPower, nShieldPower, nShieldValue, nItemValue, nRightValue; int n2HandedValue, nLeftValue, bTwoWeaponUser; int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature)); - if(AI_DEBUG) ai_Debug("0i_combat", "3054", "nMaxItemValue: " + IntToString(nMaxItemValue)); + if(AI_DEBUG) ai_Debug("0i_combat", "2997", "nMaxItemValue: " + IntToString(nMaxItemValue)); bTwoWeaponUser = GetHasFeat(374/*FEAT_DUAL_WIELD*/, oCreature) || GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oCreature); object oShield = OBJECT_INVALID; object oRight = OBJECT_INVALID; @@ -2622,7 +3145,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) } else f2HandedPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand, TRUE); n2HandedValue = GetGoldPieceValue(oRightHand); - if(AI_DEBUG) ai_Debug("0i_combat", "3073", " 2Handed oRightHand: " + GetName(oRightHand) + + if(AI_DEBUG) ai_Debug("0i_combat", "3016", " 2Handed oRightHand: " + GetName(oRightHand) + " f2HandPower: " + FloatToString(f2HandedPower, 0, 2) + " n2HandedValue: " + IntToString(n2HandedValue)); } @@ -2630,7 +3153,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) { fRightPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand); nRightValue = GetGoldPieceValue(oRightHand); - if(AI_DEBUG) ai_Debug("0i_combat", "3081", " 1Handed oRightHand: " + GetName(oRightHand) + + if(AI_DEBUG) ai_Debug("0i_combat", "3024", " 1Handed oRightHand: " + GetName(oRightHand) + " fRightPower: " + FloatToString(fRightPower, 0, 2) + " nRightValue: " + IntToString(nRightValue)); } @@ -2643,7 +3166,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) { nShieldPower = ai_SetShieldAC(oCreature, oLeftHand); nShieldValue = GetGoldPieceValue(oLeftHand); - if(AI_DEBUG) ai_Debug("0i_combat", "3098", " Shield oLeftHand: " + GetName(oLeftHand) + + if(AI_DEBUG) ai_Debug("0i_combat", "3037", " Shield oLeftHand: " + GetName(oLeftHand) + " fShieldPower: " + IntToString(nShieldPower) + " nShieldValue: " + IntToString(nShieldValue)); } @@ -2652,7 +3175,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) { fLeftPower = ai_GetMeleeWeaponAvgDmg(oCreature, oLeftHand, FALSE, TRUE); nLeftValue = GetGoldPieceValue(oLeftHand); - if(AI_DEBUG) ai_Debug("0i_combat", "3103", " 1Handed oLeftHand: " + GetName(oLeftHand) + + if(AI_DEBUG) ai_Debug("0i_combat", "3046", " 1Handed oLeftHand: " + GetName(oLeftHand) + " fLeftPower: " + FloatToString(fLeftPower, 0, 2) + " nLeftValue: " + IntToString(nLeftValue)); } @@ -2664,7 +3187,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) if(oItem == OBJECT_INVALID) return FALSE; while(oItem != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "3114", GetName(oItem) + " MeleeWeapon: " + + if(AI_DEBUG) ai_Debug("0i_combat", "3058", GetName(oItem) + " MeleeWeapon: " + IntToString(ai_GetIsMeleeWeapon(oItem)) + " Proficient: " + IntToString(ai_GetIsProficientWith(oCreature, oItem)) + " Identified: " + IntToString(GetIdentified(oItem))); @@ -2672,7 +3195,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) GetIdentified(oItem) && ai_CheckIfCanUseItem(oCreature, oItem)) { nItemValue = GetGoldPieceValue(oItem); - if(AI_DEBUG) ai_Debug("0i_combat", "3122", " nItemValue: " + IntToString(nItemValue)); + if(AI_DEBUG) ai_Debug("0i_combat", "3066", " nItemValue: " + IntToString(nItemValue)); if(!GetLocalInt(GetModule(), AI_RULE_ILR) || nMaxItemValue >= nItemValue) { if(ai_GetIsShield(oItem)) @@ -2755,7 +3278,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) } oItem = GetNextItemInInventory(); } - if(AI_DEBUG) ai_Debug("0i_combat", "3197", "oRight: " + GetName(oRight) + " oLeft:" + + if(AI_DEBUG) ai_Debug("0i_combat", "3149", "oRight: " + GetName(oRight) + " oLeft:" + GetName(oLeft) + " oShield: " + GetName(oShield) + "o2Handed: " + GetName(o2Handed)); // First check for two weapons first. @@ -2763,12 +3286,12 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) { fRightPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRight, FALSE, FALSE, oLeft); fRightPower += ai_GetMeleeWeaponAvgDmg(oCreature, oLeft, FALSE, TRUE); - if(AI_DEBUG) ai_Debug("0i_combat", "3205", " Right/Left Power: " + + if(AI_DEBUG) ai_Debug("0i_combat", "3157", " Right/Left Power: " + FloatToString(fRightPower, 0, 2) + " 2HandedPower: " + FloatToString(f2HandedPower, 0, 2)); if(fRightPower > f2HandedPower) { - if(AI_DEBUG) ai_Debug("0i_combat", "3210", GetName(oCreature) + " is equiping " + + if(AI_DEBUG) ai_Debug("0i_combat", "3162", GetName(oCreature) + " is equiping " + GetName(oRight) + " in the right hand and " + GetName(oLeft) + " in the left hand."); ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND); @@ -2778,7 +3301,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) } if(f2HandedPower > fRightPower && o2Handed != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "3220", GetName(oCreature) + " is equiping " + + if(AI_DEBUG) ai_Debug("0i_combat", "3172", GetName(oCreature) + " is equiping " + GetName(o2Handed) + " in both hands."); ActionEquipItem(o2Handed, INVENTORY_SLOT_RIGHTHAND); return TRUE; @@ -2786,7 +3309,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) // Now lets just equip the best weapon for the right hand. if(oRight != OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "3228", GetName(oCreature) + " is equiping " + + if(AI_DEBUG) ai_Debug("0i_combat", "3180", GetName(oCreature) + " is equiping " + GetName(oRight) + " in the right hand. "); ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND); } @@ -2796,7 +3319,7 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) !ai_GetIsTwoHandedWeapon(oRightHand, oCreature)) && oShield != OBJECT_INVALID && GetHasFeat(FEAT_SHIELD_PROFICIENCY, oCreature)) { - if(AI_DEBUG) ai_Debug("0i_combat", "3238", GetName(oCreature) + " is equiping " + + if(AI_DEBUG) ai_Debug("0i_combat", "3190", GetName(oCreature) + " is equiping " + GetName(oShield) + " in the left hand."); ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); return TRUE; @@ -2805,23 +3328,23 @@ int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) // holding a bow. else if(oRight == OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "3247", GetName(oCreature) + " did not equip a melee weapon"); + if(AI_DEBUG) ai_Debug("0i_combat", "3199", GetName(oCreature) + " did not equip a melee weapon"); // We couldn't find a melee weapon but we are looking to go into melee // I'm holding a ranged weapon! We better put it up. if(GetWeaponRanged(oRightHand)) { - if(AI_DEBUG) ai_Debug("0i_combat", "3252", GetName(oCreature) + " is unequiping " + GetName(oRightHand)); + if(AI_DEBUG) ai_Debug("0i_combat", "3204", GetName(oCreature) + " is unequiping " + GetName(oRightHand)); ActionUnequipItem(oRightHand); return TRUE; } } - if(AI_DEBUG) ai_Debug("0i_combat", "3257", GetName(oCreature) + " is not equiping a weapon!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3209", GetName(oCreature) + " is not equiping a weapon!"); return FALSE; } int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) { if(ai_GetAIMode(oCreature, AI_MODE_EQUIP_WEAPON_OFF)) return FALSE; - if(AI_DEBUG) ai_Debug("0i_combat", "3267", GetName(oCreature) + " is looking for best ranged weapon!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3215", GetName(oCreature) + " is looking for best ranged weapon!"); int nAmmo, nAmmoSlot, nBestType1, nBestType2, nType, nFeat, nItemValue, nRangedValue; int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature)); string sAmmo; @@ -2857,7 +3380,7 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) else if(GetHasFeat(FEAT_RAPID_RELOAD, oCreature)) { nBestType1 = BASE_ITEM_HEAVYCROSSBOW; nBestType2 = BASE_ITEM_LIGHTCROSSBOW; nAmmo = BASE_ITEM_BOLT; nAmmoSlot = INVENTORY_SLOT_BOLTS; sAmmo = "bolt"; } - if(AI_DEBUG) ai_Debug("0i_combat", "3262", "nBestType1: " + IntToString(nBestType1) + " nBestType2: " + IntToString(nBestType2) + + if(AI_DEBUG) ai_Debug("0i_combat", "3251", "nBestType1: " + IntToString(nBestType1) + " nBestType2: " + IntToString(nBestType2) + " nAmmo: " + IntToString(nAmmo)); int nCreatureSize = GetCreatureSize(oCreature) + 1; // Cycle through the inventory looking for a ranged weapon. @@ -2865,13 +3388,13 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) while(oItem != OBJECT_INVALID) { nType = GetBaseItemType(oItem); - if(AI_DEBUG) ai_Debug("0i_combat", "3269", "oItem: " + GetName(oItem) + + if(AI_DEBUG) ai_Debug("0i_combat", "3259", "oItem: " + GetName(oItem) + " Identified: " + IntToString(GetIdentified(oItem)) + " Ranged Weapon: " + Get2DAString("baseitems", "RangedWeapon", nType)); // Make sure it is identified and it is a ranged weapon. if(GetIdentified(oItem) && Get2DAString("baseitems", "RangedWeapon", nType) != "") { - if(AI_DEBUG) ai_Debug("0i_combat", "3278", " Proficient: " + + if(AI_DEBUG) ai_Debug("0i_combat", "3265", " Proficient: " + IntToString(ai_GetIsProficientWith(oCreature, oItem)) + " nMaxItemValue: " + IntToString(nMaxItemValue)); if(ai_GetIsProficientWith(oCreature, oItem)) @@ -2879,15 +3402,15 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) if(ai_CheckIfCanUseItem(oCreature, oItem)) { nItemValue = GetGoldPieceValue(oItem); - if(AI_DEBUG) ai_Debug("0i_combat", "3284", "nItemValue: " + IntToString(nItemValue)); + if(AI_DEBUG) ai_Debug("0i_combat", "3273", "nItemValue: " + IntToString(nItemValue)); if(!GetLocalInt(GetModule(), AI_RULE_ILR) || nMaxItemValue >= nItemValue) { - if(AI_DEBUG) ai_Debug("0i_combat", "3287", " Creature Size: " + IntToString(nCreatureSize) + + if(AI_DEBUG) ai_Debug("0i_combat", "3276", " Creature Size: " + IntToString(nCreatureSize) + " Weapon Size: " + Get2DAString("baseitems", "WeaponSize", nType)); // Make sure they are large enough to use it. if(StringToInt(Get2DAString("baseitems", "WeaponSize", nType)) <= nCreatureSize) { - if(AI_DEBUG) ai_Debug("0i_combat", "3292", "nItemValue: " + IntToString(nItemValue) + + if(AI_DEBUG) ai_Debug("0i_combat", "3281", "nItemValue: " + IntToString(nItemValue) + " nRangedValue: " + IntToString(nRangedValue) + " nType: " + IntToString(nType)); // Is it of the best range weapon type? 0 is any range weapon. // Also grab any range weapon until we have a best type. @@ -2899,7 +3422,7 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) if(ai_GetHasItemProperty(oItem, ITEM_PROPERTY_UNLIMITED_AMMUNITION)) { oRanged = oItem; nRangedValue = nItemValue; - if(AI_DEBUG) ai_Debug("0i_combat", "3304", "Selecting oRanged: " + GetName(oRanged) + + if(AI_DEBUG) ai_Debug("0i_combat", "3293", "Selecting oRanged: " + GetName(oRanged) + " nRangedValue: " + IntToString(nRangedValue) + " and doesn't need ammo!"); } else @@ -2915,7 +3438,7 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) else nAmmo = 0; } // Now do we have ammo for it? - if(AI_DEBUG) ai_Debug("0i_combat", "3320", "nAmmo: " + IntToString(nAmmo)); + if(AI_DEBUG) ai_Debug("0i_combat", "3309", "nAmmo: " + IntToString(nAmmo)); if(nAmmo > 0) { if(nAmmo == BASE_ITEM_ARROW || @@ -2936,7 +3459,7 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) if(oAmmo != OBJECT_INVALID) { oRanged = oItem; nRangedValue = nItemValue; - if(AI_DEBUG) ai_Debug("0i_combat", "3307", "Selecting oRanged: " + GetName(oRanged) + + if(AI_DEBUG) ai_Debug("0i_combat", "3330", "Selecting oRanged: " + GetName(oRanged) + " nRangedValue: " + IntToString(nRangedValue)); } } @@ -2952,7 +3475,7 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) // They don't have a range weapon so lets break out. if(oRanged == OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "3357", GetName(oCreature) + " did not equip a ranged weapon!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3346", GetName(oCreature) + " did not equip a ranged weapon!"); return FALSE; } ActionEquipItem(oRanged, INVENTORY_SLOT_RIGHTHAND); @@ -2960,7 +3483,7 @@ int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID) } int ai_EquipBestMonkMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "2949", GetName(OBJECT_SELF) + " is equiping best monk melee weapon!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3354", GetName(OBJECT_SELF) + " is equiping best monk melee weapon!"); int nValue, nRightValue; int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature)); object oRight = OBJECT_INVALID; @@ -2994,10 +3517,10 @@ int ai_EquipBestMonkMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALI // Finally lets just equip the kama if we have one. if(oRight == OBJECT_INVALID) { - if(AI_DEBUG) ai_Debug("0i_combat", "2983", GetName(oCreature) + " did not equip a melee weapon!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3388", GetName(oCreature) + " did not equip a melee weapon!"); return FALSE; } - if(AI_DEBUG) ai_Debug("0i_combat", "2986", GetName(oCreature) + " is equiping " + GetName(oRight) + " in the right hand."); + if(AI_DEBUG) ai_Debug("0i_combat", "3391", GetName(oCreature) + " is equiping " + GetName(oRight) + " in the right hand."); ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND); return TRUE; } @@ -3032,7 +3555,7 @@ int ai_IsInADangerousAOE(object oCreature, float fMaxRange = AI_RANGE_BATTLEFIEL else if(sAOEType == "VFX_PER_DELAY_BLAST_FIREBALL") { fRadius = 2.0; nSpell = SPELL_DELAYED_BLAST_FIREBALL; } else if(sAOEType == "VFX_PER_GLYPH") { fRadius = 2.5; nSpell = SPELL_GLYPH_OF_WARDING; } else fRadius = 0.0; - if(AI_DEBUG) ai_Debug("0i_combat", "3088", GetName(oCreature) + " distance from AOE is " + FloatToString(fDistance, 0, 2) + + if(AI_DEBUG) ai_Debug("0i_combat", "3426", GetName(oCreature) + " distance from AOE is " + FloatToString(fDistance, 0, 2) + " AOE Radius: " + FloatToString(fRadius, 0, 2) + " AOE Type: " + GetTag(oAOE)); // fRadius > 0.0 keeps them from tiggering that they are in a dangerous @@ -3066,17 +3589,17 @@ int ai_IsInADangerousAOE(object oCreature, float fMaxRange = AI_RANGE_BATTLEFIEL } else // we must find a target out of the AOE or fight in the AOE. { - oTarget = ai_GetNearestTargetNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, TRUE); + oTarget = ai_GetNearestPhysicalTargetNotInAOE(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, TRUE); if(oTarget != OBJECT_INVALID) lLocation = GetLocation(oTarget); } } else lLocation = GetRandomLocation(GetArea(oCreature), oCreature, fRadius + 1.0); ai_ClearCreatureActions(); - if(AI_DEBUG) ai_Debug("0i_combat", "3035", GetName(oCreature) + " is moving out of area of effect!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3466", GetName(oCreature) + " is moving out of area of effect!"); ActionMoveToLocation(lLocation, TRUE); return TRUE; } - else if(bDangerous) return TRUE; + else return bDangerous; return FALSE; } int ai_GetIsHidden(object oHidden) @@ -3207,11 +3730,12 @@ void ai_SetCreatureAIScript(object oCreature) { sCombatAI = "ai_defensive"; } + /* Needs looked at as it will soft lock some monsters! else if(GetHasSpell(SPELL_LESSER_DISPEL, oCreature) || GetHasSpell(SPELL_DISPEL_MAGIC, oCreature) || GetHasSpell(SPELL_GREATER_DISPELLING, oCreature)) { sCombatAI = "ai_cntrspell"; - } + }*/ else if(ai_CheckClassType(oCreature, AI_CLASS_TYPE_ARCANE) && ai_GetCharacterLevels(oCreature) > 4) sCombatAI = "ai_ranged"; else if(ai_EquipBestRangedWeapon(oCreature)) sCombatAI = "ai_ranged"; @@ -3345,6 +3869,17 @@ int ai_ACAdjustmentGood(object oCreature, object oTarget, float fACAdj) // Anything less than 1 helps are AC! return fAdjChance < 1.00; } +int ai_ACAdjustmentPenalty(object oCreature, object oTarget, float fACAdj) +{ + float fCreatureAC = IntToFloat(GetAC(oCreature)); + float fTargetAtk = IntToFloat(ai_GetCreatureAttackBonus(oTarget)); + float fToHitChance = (21.0-(fCreatureAC - fTargetAtk + fACAdj))/20.0; + float fRoll = IntToFloat(d100()); + if(AI_DEBUG) ai_Debug("0i_combat", "3380", "Adjusted Chance To Hit: " + FloatToString(fToHitChance, 0, 2) + " < " + + FloatToString(fRoll, 0, 2) + " return TRUE"); + // Roll % chance to see if we should ignore the penalty and go for it! + return fToHitChance < fRoll; +} int ai_CanIMoveInCombat(object oCreature) { // DC 15 tumble check is required to not give attacks of opportunity. @@ -3367,7 +3902,44 @@ int ai_CheckRangedCombatPosition(object oCreature, object oTarget, int nAction) float fDistance = GetDistanceBetween(oCreature, oNearestEnemy); if(AI_DEBUG) ai_Debug("0i_combat", "3337", "oNearestEnemy: " + GetName(oNearestEnemy) + " fDistance: " + FloatToString(fDistance, 0, 2)); - // If we have sneak attack then we want to be within 30'. + // If we have Point blank shot then we want to be within 15' [4 meters]. + if(GetHasFeat(FEAT_POINT_BLANK_SHOT, oCreature)) + { + if(fDistance > AI_RANGE_MELEE) + { + // We check this because if the enemy is moving or has not + // started acting then we don't want to move up on them as they + // might move towards us. Just attack! Only sneak attack if they are busy. + int nAction = GetCurrentAction(oNearestEnemy); + if(AI_DEBUG) ai_Debug("0i_combat", "3353", GetName(oNearestEnemy) + " current action: " + IntToString(nAction)); + if(nAction == ACTION_MOVETOPOINT || + nAction == ACTION_INVALID || + nAction == ACTION_RANDOMWALK) return FALSE; + // If they are attacking make sure it is in melee? + // If not then don't move since they might be moving toward us. + if(nAction == ACTION_ATTACKOBJECT) + { + if(!ai_GetNumOfEnemiesInRange(oNearestEnemy)) return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_combat", "3355", GetName(oCreature) + " is moving closer [4.0] to " + + GetName(oNearestEnemy) + " to use Point blank shot with a ranged weapon."); + ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE); + ActionMoveToObject(oNearestEnemy, TRUE, AI_RANGE_CLOSE); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + } + else if(fDistance < AI_RANGE_LONG) + { + // Lets move back a little, too far and we miss attacks! + if(AI_DEBUG) ai_Debug("0i_combat", "3374", GetName(oCreature) + " is moving away from " + + GetName(oNearestEnemy) + "[2.0] to use a ranged weapon."); + ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE); + ActionMoveAwayFromObject(oNearestEnemy, TRUE, 2.0); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + // If we have sneak attack then we want to be within 30' [9 meters]. if(GetHasFeat(FEAT_SNEAK_ATTACK, oCreature)) { if(fDistance > AI_RANGE_CLOSE) @@ -3432,7 +4004,7 @@ int ai_CheckRangedCombatPosition(object oCreature, object oTarget, int nAction) ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); return TRUE; } -} + } return FALSE; } int ai_CheckMeleeCombatPosition(object oCreature, object oTarget, int nAction, int nBaseItemType = 0) @@ -3496,3 +4068,246 @@ int ai_CheckCombatPosition(object oCreature, object oTarget, int nInMelee, int n // If we are in melee we might need to move out of combat. return ai_CheckMeleeCombatPosition(oCreature, oTarget, nAction, nBaseItemType); } +//:: Calculates total Shield AC bonuses from all sources +int ai_GetTotalShieldACBonus(object oCreature) +{ + int nShieldBonus = 0; + object oItem; + // Check left hand for shield + oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + if (GetIsObjectValid(oItem)) + { + int nBaseItem = GetBaseItemType(oItem); + if (nBaseItem == BASE_ITEM_SMALLSHIELD || + nBaseItem == BASE_ITEM_LARGESHIELD || + nBaseItem == BASE_ITEM_TOWERSHIELD) + { + nShieldBonus += GetItemACValue(oItem); + if(AI_DEBUG) ai_Debug("0i_combat", "3534","ai_GetTotalShieldACBonus: Found Shield AC, bonus = " + IntToString(nShieldBonus)+"."); + } + } + // Check creature weapon slots for shield AC bonus + oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oCreature); + if(GetIsObjectValid(oItem)) nShieldBonus += GetItemACValue(oItem); + oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCreature); + if(GetIsObjectValid(oItem)) nShieldBonus += GetItemACValue(oItem); + oItem = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCreature); + if(GetIsObjectValid(oItem)) nShieldBonus += GetItemACValue(oItem); + // Add shield AC bonuses from magical effects + effect eEffect = GetFirstEffect(oCreature); + while(GetIsEffectValid(eEffect)) + { + int nACType = GetEffectInteger(eEffect, 0); + int nACAmount = GetEffectInteger(eEffect, 1); + if(GetEffectType(eEffect) == EFFECT_TYPE_AC_INCREASE && nACType == AC_SHIELD_ENCHANTMENT_BONUS) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3560", "ai_GetTotalShieldACBonus: Found Shield AC effect, bonus = " + IntToString(nACAmount)+"."); + nShieldBonus += nACAmount; + } + eEffect = GetNextEffect(oCreature); + } + return nShieldBonus; +} +//:: [PRC8] Returns special grapple size modifier +int ai_GetSpecialGrappleSizeMod(int nSize) +{ + switch(nSize) + { + case -1 /* CREATURE_SIZE_FINE */: return -16; + case 0 /* CREATURE_SIZE_DIMINUTIVE */: return -12; + case CREATURE_SIZE_TINY: return -8; + case CREATURE_SIZE_SMALL: return -4; + case CREATURE_SIZE_MEDIUM: return 0; + case CREATURE_SIZE_LARGE: return 4; + case CREATURE_SIZE_HUGE: return 8; + case 6 /* CREATURE_SIZE_GARGANTUAN */: return 12; + case 7 /* CREATURE_SIZE_COLOSSAL */: return 16; + } + return 0; +} +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Tripping +int ai_CanITrip(object oCreature, object oTarget) +{ + //:: Sanity check + if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE; + if(!GetHasFeat(2822, oCreature)) + return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3594", "ai_CanITrip: Sanity Checks PASSED!"); + //:: Get stats + int nStrTripper = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + int nStrTarget = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + int nDexTarget = GetAbilityModifier(ABILITY_DEXTERITY, oTarget); + //:: Defender uses best of STR or DEX + int nDefenderMod = (nDexTarget > nStrTarget) ? nDexTarget : nStrTarget; + //:: Tripper bonuses + if (GetHasFeat(2807, oCreature)) //:: FEAT_IMPROVED_TRIP + { // +4 from Improved Trip + nStrTripper += 4; + } + //:: Get sizes + int nSizeTripper = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: Size mod = 4 * (size - medium) + nStrTripper += (nSizeTripper - CREATURE_SIZE_MEDIUM) * 4; + nDefenderMod += (nSizeTarget - CREATURE_SIZE_MEDIUM) * 4; + int nStability = 0; + //:: Extra Stability if creature is dwarf or quadruped + if(GetRacialType(oTarget) == RACIAL_TYPE_DWARF || + GetRacialType(oTarget) == RACIAL_TYPE_ANIMAL || + GetRacialType(oTarget) == RACIAL_TYPE_MAGICAL_BEAST || + GetRacialType(oTarget) == RACIAL_TYPE_VERMIN) + { + nStability = 4; + } + nDefenderMod += nStability; + //:: If effective trip mod is too much lower, don't bother + if(nStrTripper + 2 < nDefenderMod) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3632", "ai_CanITrip: Size Checks PASSED!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3634", "ai_CanITrip: Can use Trip on oTarget."); + return TRUE; +} +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Awesome Blow +int ai_CanIAwesomeBlow(object oCreature, object oTarget) +{ + //:: Sanity check + if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3642", "ai_CanIAwesomeBlow: Sanity check PASSED!"); + //:: Can't Awesome Blow incorporeal creatures + if(GetLocalInt(oTarget, "X2_L_IS_INCORPOREAL")) return FALSE; + if(ai_GetHasEffectType(oTarget, EFFECT_TYPE_ETHEREAL)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3650", "ai_CanIAwesomeBlow: Incorporealty check PASSED!"); + //:: Get sizes + int nSizeCreature = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: Must be larger than target + if (nSizeCreature <= nSizeTarget) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3660", "ai_CanIAwesomeBlow: Size check PASSED!"); + //:: Get target's AC + int nTargetAC = GetAC(oTarget); + //:: Awesome Blow applies -4 penalty + int nAttackBonus = GetBaseAttackBonus(oCreature) - 4; + //:: Calculate average hit chance. + int nHitChance = 10 + nAttackBonus - nTargetAC; + //:: If hit chance <= 50%, don’t bother + if (nHitChance <= 5) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3674", "ai_CanIAwesomeBlow: Attack check PASSED!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3676", "ai_CanIAwesomeBlow: Can use Awesome Blow on oTarget."); + return TRUE; +} +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Overrun +int ai_CanIOverrun(object oCreature, object oTarget) +{ + //:: FEAT_OVERRUN (2811) + //:: FEAT_IMPROVED_OVERRUN (2805) + //:: Sanity check + if (!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "1281", "ai_CanIOverrun: Sanity Check PASSED!"); + //:: Get stats + int nStrRunner = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + int nStrTarget = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + int nDexTarget = GetAbilityModifier(ABILITY_DEXTERITY, oTarget); + //:: Defender uses best of STR or DEX + int nDefenderMod = (nDexTarget > nStrTarget) ? nDexTarget : nStrTarget; + //:: Overruner bonuses + if (GetHasFeat(2805, oCreature)) + { // +4 from Improved Overrun + nStrRunner += 4; + } + //:: Get sizes + int nSizeRunner = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: Size mod = 4 * (size - medium) + nStrRunner += (nSizeRunner - CREATURE_SIZE_MEDIUM) * 4; + nDefenderMod += (nSizeTarget - CREATURE_SIZE_MEDIUM) * 4; + int nStability = 0; + //:: Extra Stability if creature is dwarf or quadruped + if(GetRacialType(oTarget) == RACIAL_TYPE_DWARF || + GetRacialType(oTarget) == RACIAL_TYPE_ANIMAL || + GetRacialType(oTarget) == RACIAL_TYPE_MAGICAL_BEAST || + GetRacialType(oTarget) == RACIAL_TYPE_VERMIN) + { + nStability = 4; + } + nDefenderMod += nStability; + //:: If effective STR mod is too much lower, don't bother + if(nStrRunner + 2 < nDefenderMod) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3731", "ai_CanIOverrun: Size & Stabilty Check PASSED!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3733", "ai_CanIOverrun: Using Overrun on oTarget."); + return TRUE; +} +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Bullrush +int ai_CanIBullrush(object oCreature, object oTarget) +{ + //:: FEAT_BULLRUSH (2810) + //:: FEAT_IMPROVED_BULLRUSH (2806) + //:: FEAT_BULLRUSH_CHARGE (3413) + if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3746", "ai_CanIBullrush: Sanity Check PASSED!"); + //:: Get movement speed + //int nSpeed = StringToInt(Get2DAString("racialtypes", "Endurance", GetRacialType(oCreature))); + float fSpeed = IntToFloat(10 * GetMovementRate(oCreature)); + //:: If target is too far away to reach in one round, return + if(GetDistanceBetween(oCreature, oTarget) > 1.5 * fSpeed) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3755", "ai_CanIBullrush: Distance Check PASSED!"); + //:: Get attacker Strength modifier + int nStrRusher = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + //:: Defender uses best of STR or DEX + int nStrTarget = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + int nDexTarget = GetAbilityModifier(ABILITY_DEXTERITY, oTarget); + int nDefenderMod = (nDexTarget > nStrTarget) ? nDexTarget : nStrTarget; + //:: Improved Bull Rush bonus + if(GetHasFeat(2806 /* FEAT_IMPROVED_BULLRUSH */, oCreature)) nStrRusher += 4; + //:: Size modifiers + int nSizeRusher = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + nStrRusher += (nSizeRusher - CREATURE_SIZE_MEDIUM) * 4; + nDefenderMod += (nSizeTarget - CREATURE_SIZE_MEDIUM) * 4; + + //:: Stability bonus (+4) for dwarves, quadrupeds, etc. + int nRace = GetRacialType(oTarget); + if (nRace == RACIAL_TYPE_DWARF || + nRace == RACIAL_TYPE_ANIMAL || + nRace == RACIAL_TYPE_MAGICAL_BEAST || + nRace == RACIAL_TYPE_VERMIN) + { + nDefenderMod += 4; + } + //:: Require at least +2 margin to attempt + if (nStrRusher + 2 < nDefenderMod) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3789", "ai_CanIBullrush: Size & Stability Check PASSED!"); + if(AI_DEBUG) ai_Debug("0i_combat", "3791", "ai_CanIBullrush: Can use Bullrush on oTarget."); + return TRUE; +} +//:: [PRC8] Returns TRUE if oTarget is a good candidate for Grappling +int ai_CanIGrapple(object oCreature, object oTarget) +{ + //:: FEAT_CM_GRAPPLE (3414) + //:: FEAT_IMPROVED_GRAPPLE (2804) + //:: FEAT_DEVASTATING_GRAPPLE (4246) + //:: Sanity checks + if (!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE; + if (!GetHasFeat(3414, oCreature)) return FALSE; + //:: Can't grapple incorporeal or gaseous creatures + if (GetLocalInt(oTarget, "X2_L_IS_INCORPOREAL") || ai_GetHasEffectType(oTarget, EFFECT_TYPE_ETHEREAL)) return FALSE; + int nSizeCreature = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: Automatically fail if target is two or more size categories larger + if (nSizeTarget - nSizeCreature >= 2) return FALSE; + //:: Get BAB and STR for grapple calculation + int nBAB = GetBaseAttackBonus(oCreature); + int nStrMod = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + int nSizeMod = ai_GetSpecialGrappleSizeMod(nSizeCreature); + int nBonusGrapple = 0; + if (GetHasFeat(2804 /*Improved Grapple*/, oCreature)) nBonusGrapple += 2; + if (GetHasFeat(4246 /*Devastating Grapple*/, oCreature)) nBonusGrapple += 5; + int nGrappleScore = nBAB + nStrMod + nSizeMod + nBonusGrapple; + //:: Estimate target's grapple score defensively + int nTargetStrMod = GetAbilityModifier(ABILITY_STRENGTH, oTarget); + int nTargetSizeMod = ai_GetSpecialGrappleSizeMod(nSizeTarget); + int nTargetBAB = GetBaseAttackBonus(oTarget); + int nTargetGrapple = nTargetBAB + nTargetStrMod + nTargetSizeMod; + int nWinChance = 10 + nGrappleScore - nTargetGrapple; + //:: If chance to win grapple check < 60%, don’t bother + if (nWinChance < 6) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3743", "Can use Grapple on oTarget."); return TRUE; +} diff --git a/src/module/nss/0i_constants.nss b/src/module/nss/0i_constants.nss index 5413ac4..59ecab6 100644 --- a/src/module/nss/0i_constants.nss +++ b/src/module/nss/0i_constants.nss @@ -7,7 +7,7 @@ Changes to any constants will not take effect until the scripts are recompiled. */////////////////////////////////////////////////////////////////////////////// -const string PHILOS_VERSION = "Philos' Enhancing Player System (PEPS) version:07.12.25"; +const string PHILOS_VERSION = "Philos' Enhancing Player System (PEPS) version:11.16.25"; // The following constants are designed to be changed to allow the AI to work // differently based on what a developer wants. // If you change these constants make sure the database has been removed @@ -18,7 +18,7 @@ const string PHILOS_VERSION = "Philos' Enhancing Player System (PEPS) version:07 // This will only work if you are using the PEPS menu system. const int AI_DEBUG = FALSE; // Defines if we are compiling for single player or a server. Always on for servers! -const int AI_SERVER = FALSE; +const int AI_SERVER = TRUE; // The number of classes allowed for a creature to take in the server/module. const int AI_MAX_CLASSES_PER_CHARACTER = 8; // Taunts cool down time before the AI attemps another Taunt. @@ -35,14 +35,14 @@ const int AI_MONSTER_HEAL_OUT_COMBAT_CHANCE = 70; const int AI_HENCHMAN_WIDGET = TRUE; // Change the Custom token number if it conflicts with your server. const int AI_BASE_CUSTOM_TOKEN = 1000; -// Delay between creatures casting Buff spells. Must be minimum of 0.1 seconds. -const float AI_HENCHMAN_BUFF_DELAY = 0.2; //******************* These can be changed within the game ******************* // Moral checks on or off. If wounded they will make Will saves, if they fail the flee. const int AI_MORAL_CHECKS = FALSE; // Allows monsters to prebuff before combat starts. const int AI_PREBUFF = TRUE; +// Allows monsters to buff with all spells before combat starts. VERY DIFFICULT! +const int AI_FULL_BUFF = FALSE; // Allows monsters cast summons spells when prebuffing. const int AI_PRESUMMONS = TRUE; // Allows monsters to use tactical AI scripts such as ambush, flanker, ranged. @@ -111,8 +111,13 @@ const int AI_SCOUT_AHEAD_ON = TRUE; const int AI_OPEN_INVENTORY = TRUE; // Allows players to have associates pickup loot. const int AI_PICKUP_LOOT = TRUE; -// Allows players to remove a henchman. +// Allows players to take any henchman that is standing around. +const int AI_ALLOW_TAKING_HENCHMAN = FALSE; +// Allows players to remove a henchman through PEPS. const int AI_REMOVE_HENCHMAN_ON = FALSE; +// Allows players to toggle patrolling ahead via the radial menu for remove henchman. +// Used on my server as a way to toggle patrolling ahead via the radial menu. +const int AI_PATROL_AHEAD_RADIAL_OPTION = FALSE; //***************************** Health Constants ***************************** // % of health for when a creature is considered wounded. const int AI_HEALTH_WOUNDED = 50; @@ -141,7 +146,9 @@ const string AI_EFFECT_ICON_NUI = "ai_effect_icon_nui"; // The following constants are core constants and changing any of these without // understanding the whole system could cause unforseen results. // CHANGE AT YOUR OWN RISK. -// Variable used to asave a monster object for changing. +// Variable used to check if the module is running as a server. +const string AI_IS_SERVER = "AI_IS_SERVER"; +// Variable used to save a monster object for changing. const string AI_MONSTER_OBJECT = "AI_MONSTER_OBJECT"; // Variable used to save a monsters json for changing. const string AI_MONSTER_JSON = "AI_MONSTER_JSON"; @@ -160,7 +167,7 @@ const string AI_PLUGIN_SET = "AI_PLUGIN_SET"; // Monster modification variable to let PEPS know what mods are available. const string AI_MONSTER_MOD_JSON = "AI_MONSTER_MOD_JSON"; // The maximum number of henchman the code works with. -const int AI_MAX_HENCHMAN = 12; +const int AI_MAX_HENCHMAN = 30; // Delay between Henchman casting Healing spells. Must be minimum of 0.5 seconds. const float AI_HENCHMAN_HEALING_DELAY = 6.0; // A variable that can be set on creatures to stop mobile animations. @@ -219,7 +226,6 @@ const string AI_I_AM_DEAD = "AI_I_AM_DEAD"; const string AI_I_AM_DISEASED = "AI_I_AM_DISEASED"; const string AI_I_AM_POISONED = "AI_I_AM_POISONED"; const string AI_I_AM_WEAK = "AI_I_AM_WEAK"; -const int AI_ALLY_SEES_AN_ENEMY = 1; const int AI_ALLY_HEARD_AN_ENEMY = 2; const int AI_ALLY_ATKED_BY_WEAPON = 3; const int AI_ALLY_ATKED_BY_SPELL = 4; @@ -228,6 +234,7 @@ const int AI_ALLY_IS_DEAD = 6; const int AI_ALLY_IS_DISEASED = 7; const int AI_ALLY_IS_POISONED = 8; const int AI_ALLY_IS_WEAK = 9; +const int AI_ALLY_SEES_AN_ENEMY = 10; const string AI_MY_TARGET = "AI_MY_TARGET"; // Constant used by monsters to reduce checks while searching for unseen targets. const string AI_AM_I_SEARCHING = "AI_AM_I_SEARCHING"; @@ -238,6 +245,8 @@ const string AI_IS_INVISIBLE = "AI_IS_INVISIBLE"; // Constants used in combat to keep track of a creatures last action. // 0+ is the last spell cast from the line number in Spells.2da. const string sLastActionVarname = "AI_LAST_ACTION"; +// Constants used in combat to keep track of a creatures last action time. +const string sLastActionTimeVarname = "AI_LAST_ACTION_TIME"; const int AI_LAST_ACTION_CAST_SPELL = -1; const int AI_LAST_ACTION_NONE = -2; const int AI_LAST_ACTION_MELEE_ATK = -3; @@ -255,6 +264,8 @@ const string AI_ATTACKED_PHYSICAL = "AI_ATTACKED_PHYSICAL"; const string AI_ATTACKED_SPELL = "AI_ATTACKED_SPELL"; // Variable name used to keep track of a creatures normal polymorph form. const string AI_NORMAL_FORM = "AI_NORMAL_FORM"; +// Variable name used to have associates defined as Polymorphed. +const string AI_POLYMORPHED = "AI_POLYMORPHED"; // Variable name used to keep track if a creature has been buffed yet. const string AI_CASTER_BUFFS_SET = "AI_CASTER_BUFFS_SET"; // Variable name used to keep track of rounds in combat for a custom ai script. @@ -320,6 +331,7 @@ const int AI_CONDITION_CONFUSED = 0x00020000; const int AI_CONDITION_CURSE = 0x00040000; const int AI_CONDITION_PARALYZE = 0x00080000; const int AI_CONDITION_DOMINATED = 0x00100000; +const int AI_CONDITION_DEAD = 0x00200000; // Database constants for Associate modes. const string AI_MODE_DB_TABLE = "AI_MODE_DB_TABLE"; // Bitwise constants for Associate modes that are used with Get/SetAssociateMode(). @@ -394,30 +406,31 @@ const int AI_MAGIC_NO_SPONTANEOUS_CURE = 0x00000800; // Caster will stop using s const string AI_NO_NUI_SAVE = "AI_NO_NUI_SAVE"; // Bitwise menu constants for Widget buttons that are used with Get/SetAssociateWidgetButtons(). const string sWidgetButtonsVarname = "ASSOCIATE_WIDGET_BUTTONS"; -const int BTN_WIDGET_OFF = 0x00000001; // Removes the widget from the screen, For PC it removes all associates. -const int BTN_WIDGET_LOCK = 0x00000002; // Locks the widget to the current coordinates. -const int BTN_CMD_GUARD = 0x00000004; // Command associates to Guard Me. PC widget only. -const int BTN_CMD_FOLLOW = 0x00000008; // Command associates to Follow. PC widget only. -const int BTN_CMD_HOLD = 0x00000010; // Command associates to Stand Ground. PC widget only. -const int BTN_CMD_ATTACK = 0x00000020; // Command associates to Attack Nearest. PC widget only. -const int BTN_BUFF_REST = 0x00000040; // Buffs with long duration spells after resting. Associate widget only. -const int BTN_BUFF_SHORT = 0x00000080; // Buffs with short duration spells. -const int BTN_BUFF_LONG = 0x00000100; // Buffs with long duration spells. -const int BTN_BUFF_ALL = 0x00000200; // Buffs with all spells. -const int BTN_CMD_ACTION = 0x00000400; // Command associate to do an action. -const int BTN_CMD_GHOST_MODE = 0x00000800; // Toggle's associates ghost mode. -const int BTN_CMD_AI_SCRIPT = 0x00001000; // Toggle's special tactics ai scripts. -const int BTN_CMD_PLACE_TRAP = 0x00002000; // A trapper may place traps. -const int BTN_CMD_CAMERA = 0x00004000; // Places camera view on associate. -const int BTN_CMD_INVENTORY = 0x00008000; // Opens inventory of associate. -const int BTN_CMD_FAMILIAR = 0x00010000; // Summons familiar. -const int BTN_CMD_COMPANION = 0x00020000; // Summons Companion. -const int BTN_CMD_SEARCH = 0x00040000; // Command all associates to use search mode. PC widget only. -const int BTN_CMD_STEALTH = 0x00080000; // Command all associates to use stealth mode. PC widget only. -const int BTN_CMD_SCOUT = 0x00100000; // Command associate to scout ahead of the part. -const int BTN_CMD_SPELL_WIDGET = 0x00200000; // Allows adding or removing spells from Spell Widget. -const int BTN_CMD_JUMP_TO = 0x00400000; // Player can make associates jump to them. -const int BTN_WIDGET_VERTICAL = 0x80000000; // Widget will be displayed vertical. +const int BTN_WIDGET_OFF = 0x00000001; // Removes the widget from the screen, For PC it removes all associates. +const int BTN_WIDGET_LOCK = 0x00000002; // Locks the widget to the current coordinates. +const int BTN_CMD_GUARD = 0x00000004; // Command associates to Guard Me. PC widget only. +const int BTN_CMD_FOLLOW = 0x00000008; // Command associates to Follow. PC widget only. +const int BTN_CMD_HOLD = 0x00000010; // Command associates to Stand Ground. PC widget only. +const int BTN_CMD_ATTACK = 0x00000020; // Command associates to Attack Nearest. PC widget only. +const int BTN_BUFF_REST = 0x00000040; // Buffs with long duration spells after resting. Associate widget only. +const int BTN_BUFF_SHORT = 0x00000080; // Buffs with short duration spells. +const int BTN_BUFF_LONG = 0x00000100; // Buffs with long duration spells. +const int BTN_BUFF_ALL = 0x00000200; // Buffs with all spells. +const int BTN_CMD_ACTION = 0x00000400; // Command associate to do an action. +const int BTN_CMD_GHOST_MODE = 0x00000800; // Toggle's associates ghost mode. +const int BTN_CMD_AI_SCRIPT = 0x00001000; // Toggle's special tactics ai scripts. +const int BTN_CMD_PLACE_TRAP = 0x00002000; // A trapper may place traps. +const int BTN_CMD_CAMERA = 0x00004000; // Places camera view on associate. +const int BTN_CMD_INVENTORY = 0x00008000; // Opens inventory of associate. +const int BTN_CMD_FAMILIAR = 0x00010000; // Summons familiar. +const int BTN_CMD_COMPANION = 0x00020000; // Summons Companion. +const int BTN_CMD_SEARCH = 0x00040000; // Command all associates to use search mode. PC widget only. +const int BTN_CMD_STEALTH = 0x00080000; // Command all associates to use stealth mode. PC widget only. +const int BTN_CMD_SCOUT = 0x00100000; // Command associate to scout ahead of the part. +const int BTN_CMD_SPELL_WIDGET = 0x00200000; // Allows adding or removing spells from Spell Widget. +const int BTN_CMD_JUMP_TO = 0x00400000; // Player can make associates jump to them. +const int BTN_ASSOC_WIDGETS_OFF = 0x00800000; // Turns all associate widgets on/off. +const int BTN_WIDGET_VERTICAL = 0x80000000; // Widget will be displayed vertical. // Bitwise menu constants for Associate AI buttons that are used with Get/SetAssociateAIButtons(). const string sAIButtonsVarname = "ASSOCIATE_AI_BUTTONS"; const int BTN_AI_FOR_PC = 0x00000001; // PC use AI. PC widget only. @@ -521,6 +534,8 @@ const string AI_ASSOCIATE_PERCEPTION = "AI_PERCEPTION_RANGE"; const string AI_ASSOC_PERCEPTION_DISTANCE = "AI_ASSOC_PERCEPTION_DISTANCE"; // Variable that holds the open doors range of the henchman. const string AI_OPEN_DOORS_RANGE = "AI_OPEN_DOORS_RANGE"; +// Variable that holds the delay for casting buff spells. +const string AI_DELAY_BUFF_CASTING = "AI_DELAY_BUFF_CASTING"; // Variable that holds the Spell widgets json data. const string AI_SPELLS_WIDGET = "AI_SPELLS_WIDGET"; // The number of Buff Groups @@ -548,6 +563,8 @@ const string AI_PC_LOCKED_TARGET = "AI_PC_LOCKED_TARGET"; const string AI_TALENT_IMMUNITY = "AI_TALENT_IMMUNITY"; // Variables keeps track of the maximum level for the talent category. const string AI_MAX_TALENT = "AI_MAX_TALENT_"; +// Variables keeps track of the maximum level for the talent level. +const string AI_MAX_LEVEL = "AI_MAX_LEVEL_"; // Backward compatability constants. const int X2_EVENT_CONCENTRATION_BROKEN = 12400; // Variable set on the module if the module is using PRC. @@ -568,6 +585,12 @@ const string AI_MODULE_GUI_EVENT = "AI_MODULE_GUI_EVENT"; const string AI_TARGET_MODE = "AI_TARGET_MODE"; // Variable used on the player to define which associate triggered the OnPlayer Target. const string AI_TARGET_ASSOCIATE = "AI_TARGET_ASSOCIATE"; +// Variable use on the player to define if the player is using target mode on an associate. +const string AI_TARGET_MODE_ON = "AI_TARGET_MODE_ON"; +// Variable used on the player to define what associate has Target Mode set on it. +const string AI_TARGET_MODE_ASSOCIATE = "AI_TARGET_MODE_ASSOCIATE"; +// Variable used on a creature to define how long ago their immunities were saved. +const string sIPTimeStampVarname = "AI_IP_TIMESTAMP"; // Bitwise constants for immune damage item properties that is used with Get/SetItemProperty(). const string sIPImmuneVarname = "AI_IP_IMMUNE"; // Bitwise constants for resisted damage item properties that is used with Get/SetItemProperty(). @@ -585,6 +608,8 @@ const string AI_RULE_DEBUG_CREATURE = "AI_RULE_DEBUG_CREATURE"; const string AI_RULE_MORAL_CHECKS = "AI_RULE_MORAL_CHECKS"; // Allows monsters to prebuff before combat starts. const string AI_RULE_BUFF_MONSTERS = "AI_RULE_BUFF_MONSTERS"; +// Allows monsters to prebuff with all defensive spells before combat starts. +const string AI_RULE_FULL_BUFF_MONSTERS = "AI_RULE_FULL_BUFF_MONSTERS"; // Allows monsters to use the ambush AI scripts. const string AI_RULE_AMBUSH = "AI_RULE_AMBUSH"; // Enemies may summon familiars and Animal companions and will be randomized. diff --git a/src/module/nss/0i_gui_events.nss b/src/module/nss/0i_gui_events.nss index 4628cf3..bc90d25 100644 --- a/src/module/nss/0i_gui_events.nss +++ b/src/module/nss/0i_gui_events.nss @@ -12,7 +12,7 @@ *///////////////////////////////////////////////////////////////////////////////////////////////////// #include "0i_constants" #include "0i_nui" -void ai_SetupModuleGUIEvents(object oCreature) +void ai_SetupModuleGUIEvents() { object oModule = GetModule(); string sModuleGUIEvents = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_GUIEVENT); diff --git a/src/module/nss/0i_main.nss b/src/module/nss/0i_main.nss index a0a4674..ba36f4a 100644 --- a/src/module/nss/0i_main.nss +++ b/src/module/nss/0i_main.nss @@ -25,6 +25,8 @@ const string AI_DM_TABLE = "DM_TABLE"; // Sets PEPS RULES from the database to the module. // Creates default rules if they do not exist. void ai_SetAIRules(); +// Returns TRUE if the module is being run as a server. +int ai_GetIsServer(); // Returns TRUE if oCreature is controlled by a player. int ai_GetIsCharacter(object oCreature); // Returns TRUE if oCreature is controlled by a dungeon master. @@ -32,6 +34,9 @@ int ai_GetIsDungeonMaster(object oCreature); // Returns the Player of oAssociate even if oAssociate is the player. // If there is no player associated with oAssociate then it returns OBJECT_INVALID. object ai_GetPlayerMaster(object oAssociate); +// Returns the top master of oAssociate, for example a henchmen summons a bat, +// this will return the henchman's player. +object ai_GetTopMaster(object oAssociate); // Returns the percentage of hit points oCreature has left. int ai_GetPercHPLoss(object oCreature); // Returns a rolled result from sDice string. @@ -121,15 +126,17 @@ void ai_SetAIRules() json jRules = ai_GetCampaignDbJson("rules"); if(JsonGetType(JsonObjectGet(jRules, AI_RULE_MORAL_CHECKS)) == JSON_TYPE_NULL) { - jRules = JsonObject(); // Variable name set to a creatures full name to set debugging on. - jRules = JsonObjectSet(jRules, AI_RULE_DEBUG_CREATURE, JsonString("")); + jRules = JsonObjectSet(JsonObject(), AI_RULE_DEBUG_CREATURE, JsonString("")); // Moral checks on or off. SetLocalInt(oModule, AI_RULE_MORAL_CHECKS, AI_MORAL_CHECKS); jRules = JsonObjectSet(jRules, AI_RULE_MORAL_CHECKS, JsonInt(AI_MORAL_CHECKS)); // Allows monsters to prebuff before combat starts. SetLocalInt(oModule, AI_RULE_BUFF_MONSTERS, AI_PREBUFF); jRules = JsonObjectSet(jRules, AI_RULE_BUFF_MONSTERS, JsonInt(AI_PREBUFF)); + // Allows monsters to prebuff with all spells before combat starts. + SetLocalInt(oModule, AI_RULE_FULL_BUFF_MONSTERS, AI_FULL_BUFF); + jRules = JsonObjectSet(jRules, AI_RULE_FULL_BUFF_MONSTERS, JsonInt(AI_FULL_BUFF)); // Allows monsters cast summons spells when prebuffing. SetLocalInt(oModule, AI_RULE_PRESUMMON, AI_PRESUMMONS); jRules = JsonObjectSet(jRules, AI_RULE_PRESUMMON, JsonInt(AI_PRESUMMONS)); @@ -216,6 +223,9 @@ void ai_SetAIRules() // Allows monsters to prebuff before combat starts. bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_BUFF_MONSTERS)); SetLocalInt(oModule, AI_RULE_BUFF_MONSTERS, bValue); + // Allows monsters to buff with all spells before combat starts. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_FULL_BUFF_MONSTERS)); + SetLocalInt(oModule, AI_RULE_FULL_BUFF_MONSTERS, bValue); // Allows monsters cast summons spells when prebuffing. bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_PRESUMMON)); SetLocalInt(oModule, AI_RULE_PRESUMMON, bValue); @@ -308,13 +318,17 @@ void ai_SetAIRules() SetLocalInt(oModule, sDMAIAccessVarname, bValue); } } +int ai_GetIsServer() +{ + return GetLocalInt(GetModule(), AI_IS_SERVER) || AI_SERVER; +} int ai_GetIsCharacter(object oCreature) { - return (GetIsPC(oCreature) && !GetIsDM(oCreature) && !GetIsDMPossessed(oCreature)); + return (GetIsPC(oCreature) && !GetIsDM(oCreature) && !GetIsDMPossessed(oCreature) && !GetIsPlayerDM(oCreature)); } int ai_GetIsDungeonMaster(object oCreature) { - return (GetIsDM(oCreature) || GetIsDMPossessed(oCreature)); + return (GetIsDM(oCreature) || GetIsDMPossessed(oCreature) || GetIsPlayerDM(oCreature)); } object ai_GetPlayerMaster(object oAssociate) { @@ -323,6 +337,16 @@ object ai_GetPlayerMaster(object oAssociate) if(ai_GetIsCharacter(oMaster)) return oMaster; return OBJECT_INVALID; } +object ai_GetTopMaster(object oAssociate) +{ + object oMaster = GetMaster(oAssociate); + while(oMaster != OBJECT_INVALID) + { + if(GetMaster(oMaster) == OBJECT_INVALID) break; + oMaster = GetMaster(oMaster); + } + return oMaster; +} int ai_GetPercHPLoss(object oCreature) { int nHP = GetCurrentHitPoints(oCreature); @@ -702,28 +726,48 @@ void ai_CheckAssociateDataAndInitialize(object oPlayer, string sAssociateType) string ai_GetAssociateType(object oPlayer, object oAssociate) { if(GetIsPC(oAssociate)) return "pc"; + int nIndex = 1; string sAITag = GetLocalString(oAssociate, AI_TAG); + object oCreature; if(sAITag == "") { int nAssociateType = GetAssociateType(oAssociate); - if(nAssociateType == ASSOCIATE_TYPE_ANIMALCOMPANION) sAITag = "companion"; - else if(nAssociateType == ASSOCIATE_TYPE_FAMILIAR) sAITag = "familiar"; - else if(nAssociateType == ASSOCIATE_TYPE_SUMMONED) sAITag = "summons"; - else if(nAssociateType == ASSOCIATE_TYPE_DOMINATED) sAITag = "dominated"; - else if(nAssociateType == ASSOCIATE_TYPE_HENCHMAN) sAITag = GetTag(oAssociate); - string sCurrentAITag; - // Check for duplicate tags and change. - int nIndex; - object oCreature; - for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + if(nAssociateType == ASSOCIATE_TYPE_HENCHMAN) { + sAITag = GetTag(oAssociate); oCreature = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPlayer, nIndex); - if(oAssociate != oCreature && sAITag == GetTag(oCreature)) sAITag += IntToString(Random(1000)); + // Check for duplicate tags and change. + while(nIndex <= AI_MAX_HENCHMAN && oCreature != OBJECT_INVALID) + { + if(oAssociate != oCreature && sAITag == GetTag(oCreature)) + { + sAITag += IntToString(Random(1000)); + break; + } + oCreature = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPlayer, ++nIndex); + } } - for(nIndex = 2; nIndex < 6; nIndex++) + else if(nAssociateType == ASSOCIATE_TYPE_SUMMONED) { - oCreature = GetAssociate(nIndex, oPlayer, 1); - if(oAssociate != oCreature && sAITag == GetTag(oCreature)) sAITag += IntToString(Random(1000)); + int nCounter; + sAITag = GetTag(oAssociate); + oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPlayer, nIndex); + while(nIndex <= 10 && oCreature != OBJECT_INVALID) + { + if(oAssociate != oCreature && sAITag == GetTag(oCreature)) + { + nCounter++; + sAITag += IntToString(nCounter); + nIndex = 0; + } + oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPlayer, ++nIndex); + } + } + else + { + if(nAssociateType == ASSOCIATE_TYPE_ANIMALCOMPANION) sAITag = "companion"; + else if(nAssociateType == ASSOCIATE_TYPE_FAMILIAR) sAITag = "familiar"; + else if(nAssociateType == ASSOCIATE_TYPE_DOMINATED) sAITag = "dominated"; } SetLocalString(oAssociate, AI_TAG, sAITag); } @@ -773,7 +817,7 @@ float ai_GetAssociateDbFloat(object oPlayer, string sAssociatetype, string sData } void ai_SetAssociateDbJson(object oPlayer, string sAssociateType, string sDataField, json jData, string sTable = AI_TABLE) { - //SendMessageToPC(oPlayer, "0i_main, 629, Set DbJson - sAssociateType: " + sAssociateType + " sDataField: " + sDataField + " jData: " + JsonDump(jData)); + //SendMessageToPC(oPlayer, "0i_main, 777, Set DbJson - sAssociateType: " + sAssociateType + " sDataField: " + sDataField + " jData: " + JsonDump(jData)); string sQuery = "UPDATE " + sTable + " SET " + sDataField + " = @data WHERE name = @name;"; sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); @@ -783,7 +827,7 @@ void ai_SetAssociateDbJson(object oPlayer, string sAssociateType, string sDataFi } json ai_GetAssociateDbJson(object oPlayer, string sAssociateType, string sDataField, string sTable = AI_TABLE) { - //SendMessageToPC(oPlayer, "0i_main, 638, Get DbJson - sAssociateType: " + sAssociateType + " sDataField: " + sDataField); + //SendMessageToPC(oPlayer, "0i_main, 787, Get DbJson - sAssociateType: " + sAssociateType + " sDataField: " + sDataField); string sQuery = "SELECT " + sDataField + " FROM " + sTable + " WHERE name = @name;"; sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); SqlBindString (sql, "@name", sAssociateType); @@ -843,12 +887,14 @@ void ai_SetupAIData(object oPlayer, object oAssociate, string sAssociateType) // We keep it for now as we don't want to move other data. jAIData = JsonArrayInsert(jAIData, JsonInt(11)); // 7 - Associate Perception DistanceDistance. SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION, 11); - SetLocalFloat(oAssociate, AI_ASSOC_PERCEPTION_DISTANCE, 20.0); + SetLocalFloat(oAssociate, AI_ASSOC_PERCEPTION_DISTANCE, 25.0); jAIData = JsonArrayInsert(jAIData, JsonString("")); // 8 - Associate Combat Tactics. jAIData = JsonArrayInsert(jAIData, JsonFloat(20.0)); // 9 - Open Doors check range. SetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE, 20.0); json jSpells = JsonArray(); jAIData = JsonArrayInsert(jAIData, jSpells); // 10 - Castable spells. + jAIData = JsonArrayInsert(jAIData, JsonFloat(0.1)); // 11 - Delay for casting buff spells. + SetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING, 0.1); ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData, AI_TABLE); } void ai_SetupLootFilters(object oPlayer, object oAssociate, string sAssociateType) @@ -960,6 +1006,8 @@ void ai_RestoreDatabase(object oPlayer, object oAssociate, string sAssociateType SetLocalJson(oPlayer, AI_SPELLS_WIDGET, jValue); } jAIData = JsonArrayInsert(jAIData, jValue); + fValue = GetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING); + jAIData = JsonArrayInsert(jAIData, JsonFloat(fValue)); ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData); // ********** LootFilters ********** json jLootFilters = JsonArray(); @@ -1061,6 +1109,14 @@ void ai_CheckAssociateData(object oPlayer, object oAssociate, string sAssociateT ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData); SetLocalJson(oPlayer, AI_SPELLS_WIDGET, jSpellsWidget); } + json jSpellDelay = JsonArrayGet(jAIData, 11); + if(JsonGetType(jSpellDelay) == JSON_TYPE_NULL) + { + jAIData = JsonArrayInsert(jAIData, JsonFloat(0.1)); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData); + SetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING, 0.1); + } + else SetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING, JsonGetFloat(jSpellDelay)); } // ********** LootFilters ********** json jLootFilters = ai_GetAssociateDbJson(oPlayer, sAssociateType, "lootfilters"); @@ -1082,7 +1138,7 @@ void ai_CheckAssociateData(object oPlayer, object oAssociate, string sAssociateT // These are pulled straight from the database. // ********** Locations ********** json jLocations = ai_GetAssociateDbJson(oPlayer, sAssociateType, "locations"); - if(JsonGetType(JsonObjectGet(jLocations, AI_WIDGET_NUI)) == JSON_TYPE_NULL) + if(JsonGetType(JsonObjectGet(jLocations, sAssociateType + AI_WIDGET_NUI)) == JSON_TYPE_NULL) { ai_SetupLocations(oPlayer, oAssociate, sAssociateType); } @@ -1121,7 +1177,7 @@ void ai_SetupDMData(object oPlayer, string sName) void ai_CheckDMData(object oPlayer) { //ai_Debug("0i_main", "898", "Checking data for DM: " + GetName(oPlayer)); - string sName = ai_RemoveIllegalCharacters(GetName(oPlayer)); + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer))); // ********** Buttons ********** json jButtons = ai_GetCampaignDbJson("buttons", sName, AI_DM_TABLE); // if there is no saved AImodes then set the defaults. @@ -1222,7 +1278,7 @@ json ai_CheckOldPluginJson(object oPC) json ai_UpdatePluginsForPC(object oPC) { // Check if the server is running or single player. - if(!AI_SERVER) return ai_CheckOldPluginJson(oPC); + if(!ai_GetIsServer()) return ai_CheckOldPluginJson(oPC); int nJsonType, nCounter, nIndex, bWidget, bAllow; string sScript, sName, sIcon; json jServerPlugins = ai_GetCampaignDbJson("plugins"); @@ -1264,7 +1320,7 @@ json ai_UpdatePluginsForPC(object oPC) json ai_UpdatePluginsForDM(object oPC) { int nJsonType, nCounter, nIndex, bWidget, bAllow; - string sName, sIcon, sDbName = ai_RemoveIllegalCharacters(GetName(oPC)); + string sName, sIcon, sDbName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPC))); json jServerPlugins = ai_GetCampaignDbJson("plugins"); ai_CheckDMDataAndInitialize(oPC); json jDMPlugin, jDMPlugins = ai_GetCampaignDbJson("plugins", sDbName, AI_DM_TABLE); @@ -1304,7 +1360,7 @@ void ai_StartupPlugins(object oPC) int bUpdatePlugins; string sScript; json jPlugins; - if(GetIsDM(oPC)) jPlugins = ai_UpdatePluginsForDM(oPC); + if(ai_GetIsDungeonMaster(oPC)) jPlugins = ai_UpdatePluginsForDM(oPC); else jPlugins = ai_UpdatePluginsForPC(oPC); // We delete this so each mod can be added that legally loads. DeleteLocalJson(GetModule(), AI_MONSTER_MOD_JSON); diff --git a/src/module/nss/0i_menus.nss b/src/module/nss/0i_menus.nss index 8d6834c..472d0dd 100644 --- a/src/module/nss/0i_menus.nss +++ b/src/module/nss/0i_menus.nss @@ -26,6 +26,8 @@ int ai_GetAIButton2(object oPlayer, int nButton, object oAssociate, string sAsso json ai_CreateCompanionJson(object oPC, string sCompanion2da); // Return any Metamagic or Domain attributes to place on a spell icon image. string ai_GetSpellIconAttributes(object oCaster, int nMetaMagic, int nDomain); +// Populates the Quick widget list menu. +void ai_PopulateWidgetList(object oPC, object oAssociate, int nToken, json jWidget); // Creates the AI options menu. void ai_CreateAIMainNUI(object oPC); // Creates the AI options menu. @@ -49,7 +51,7 @@ void ai_CreateDescriptionNUI(object oPC, json jSpell, int nSpell = 0); string ai_GetRandomTip() { int nRoll; - if(AI_SERVER) nRoll = Random(26); + if(ai_GetIsServer()) nRoll = Random(26); else nRoll = Random(46); return Get2DAString("ai_messages", "Text", nRoll); } @@ -129,6 +131,211 @@ string ai_GetSpellIconAttributes(object oCaster, int nMetaMagic, int nDomain) if(nDomain > 0) sAttributeText += "D"; return sAttributeText; } +void ai_PopulateWidgetList(object oPC, object oAssociate, int nToken, json jWidget) +{ + int nSAIndex, nSpell, nClass, nFeat, nBaseItemType, nIprpSubType, nUses; + int nLevel, nMetaMagic, nDomain, nIndex; + string sIndex, sBaseName, sName, sSpellIcon, sText, sClass, sMetaMagicText; + object oItem; + json jSpell; + while(nIndex < 10) + { + jSpell = JsonArrayGet(jWidget, nIndex); + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); + if(nClass == -1) // This is an Item. + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + nBaseItemType = JsonGetInt(JsonArrayGet(jSpell, 3)); + nIprpSubType = JsonGetInt(JsonArrayGet(jSpell, 4)); + if(nSpell == SPELL_HEALINGKIT) + { + sName = "Healer's Kit +" + IntToString(nIprpSubType); + sSpellIcon = "isk_heal"; + sBaseName = "Healer's Kit"; + } + else if(nBaseItemType == BASE_ITEM_ENCHANTED_SCROLL || + nBaseItemType == BASE_ITEM_SCROLL || + nBaseItemType == BASE_ITEM_SPELLSCROLL) + { + sSpellIcon = Get2DAString("iprp_spells", "Icon", nIprpSubType); + sBaseName = "Scroll"; + } + else + { + if(nBaseItemType == BASE_ITEM_ENCHANTED_POTION || + nBaseItemType == BASE_ITEM_POTIONS) sBaseName = "Potion"; + else if(nBaseItemType == BASE_ITEM_ENCHANTED_WAND || + nBaseItemType == BASE_ITEM_MAGICWAND || + nBaseItemType == FEAT_CRAFT_WAND) sBaseName = "Wand"; + else sBaseName = ai_StripColorCodes(GetName(GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))))); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + } + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + nUses = ai_GetItemUses(oItem, nIprpSubType); + if(nUses) + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); + if(nUses == 999) sText = "Unlimited"; + else sText = IntToString(nUses); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + " / " + sText + ")")); + } + } + else if(nFeat) // This is a feat. + { + sSpellIcon = ""; + if(nSpell) + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + } + if(sSpellIcon == "" || sSpellIcon == "IR_USE") + { + sName = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))); + sSpellIcon = Get2DAString("feat", "ICON", nFeat); + } + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); + } + else // This is a spell. + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); + nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); + nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + if(nClass == 255) + { + nSAIndex = JsonGetInt(JsonArrayGet(jSpell, 6)); + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (Special Ability / " + IntToString(nLevel) + ")")); + } + else + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); + sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nMetaMagic, nDomain); + NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); + } + } + } + else + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString("")); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + ++nIndex; + } + if(nIndex < 10) return; + // Row 6 Quick widget List2 + while(nIndex < 20) + { + jSpell = JsonArrayGet(jWidget, nIndex); + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); + if(nClass == -1) // This is an Item. + { + string sBaseName; + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + int nBaseItemType = JsonGetInt(JsonArrayGet(jSpell, 3)); + int nIprpSubType = JsonGetInt(JsonArrayGet(jSpell, 4)); + if(nSpell == SPELL_HEALINGKIT) + { + sName = "Healer's Kit +" + IntToString(nIprpSubType); + sSpellIcon = "isk_heal"; + sBaseName = "Healer's Kit"; + } + else if(nBaseItemType == BASE_ITEM_ENCHANTED_SCROLL || + nBaseItemType == BASE_ITEM_SCROLL || + nBaseItemType == BASE_ITEM_SPELLSCROLL) + { + sSpellIcon = Get2DAString("iprp_spells", "Icon", nIprpSubType); + sBaseName = "Scroll"; + } + else + { + if(nBaseItemType == BASE_ITEM_ENCHANTED_POTION || + nBaseItemType == BASE_ITEM_POTIONS) sBaseName = "Potion"; + else if(nBaseItemType == BASE_ITEM_ENCHANTED_WAND || + nBaseItemType == BASE_ITEM_MAGICWAND || + nBaseItemType == FEAT_CRAFT_WAND) sBaseName = "Wand"; + else sBaseName = ai_StripColorCodes(GetName(GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))))); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + } + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + int nUses = ai_GetItemUses(oItem, nIprpSubType); + if(nUses) + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); + if(nUses == 999) sText = "Unlimited"; + else sText = IntToString(nUses); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + " / " + sText + ")")); + } + } + else if(nFeat) // This is a feat. + { + sSpellIcon = ""; + if(nSpell) + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + } + if(sSpellIcon == "" || sSpellIcon == "IR_USE") + { + sName = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))); + sSpellIcon = Get2DAString("feat", "ICON", nFeat); + } + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); + } + else // This is a spell. + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); + nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + if(nClass == 255) + { + nSAIndex = JsonGetInt(JsonArrayGet(jSpell, 6)); + if(GetSpellAbilityReady(oAssociate, nSAIndex)) + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (Special Ability / " + IntToString(nLevel) + ")")); + } + } + else + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); + sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nMetaMagic, nDomain); + NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); + } + } + } + else + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString("")); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + ++nIndex; + } +} void ai_CreateAIMainNUI(object oPC) { // Set window to not save until it has been created. @@ -137,7 +344,7 @@ void ai_CreateAIMainNUI(object oPC) int nMonsterAI = (ResManGetAliasFor("ai_default", RESTYPE_NCS) != ""); int nAssociateAI = (ResManGetAliasFor("ai_a_default", RESTYPE_NCS) != ""); string sText = " [Single player]"; - if(AI_SERVER) sText = " [Server]"; + if(ai_GetIsServer()) sText = " [Server]"; // ************************************************************************* Width / Height // Row 1 ******************************************************************* 500 / 73 json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); @@ -145,15 +352,17 @@ void ai_CreateAIMainNUI(object oPC) jRow = JsonArrayInsert(jRow, NuiSpacer()); // Add row to the column. json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); - // Row 2 ******************************************************************* 500 / 101 - jRow = CreateLabel(JsonArray(), "", "lbl_ai_info", 510.0f, 20.0f, NUI_HALIGN_CENTER); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + if(!AI_SERVER) + { + // Row 2 ******************************************************************* 500 / 101 + jRow = CreateLabel(JsonArray(), "", "lbl_ai_info", 510.0f, 20.0f, NUI_HALIGN_CENTER); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } // Row 3 ******************************************************************* 500 / 129 - jRow = CreateButton(JsonArray(), "Plugin Manager", "btn_plugin_manager", 120.0f, 20.0f, -1.0, "btn_plugin_manager_tooltip"); - if(nAssociateAI) jRow = CreateButtonSelect(jRow, "Associate Widgets", "btn_toggle_assoc_widget", 140.0f, 20.0f, "btn_assoc_widget_tooltip"); - jRow = CreateButtonSelect(jRow, "Action Ghost Mode", "btn_action_ghost", 160.0f, 20.0f, "btn_action_ghost_tooltip"); - jRow = CreateButtonSelect(jRow, "Effect Icons", "btn_effect_icon", 100.0f, 20.0f, "btn_effect_icon_tooltip"); + jRow = CreateButton(JsonArray(), "Plugin Manager", "btn_plugin_manager", 175.0f, 20.0f, -1.0, "btn_plugin_manager_tooltip"); + jRow = CreateButtonSelect(jRow, "Action Ghost Mode", "btn_action_ghost", 175.0f, 20.0f, "btn_action_ghost_tooltip"); + jRow = CreateButtonSelect(jRow, "Effect Icons", "btn_effect_icon", 175.0f, 20.0f, "btn_effect_icon_tooltip"); // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 4 ******************************************************************* 500 / 157 @@ -201,7 +410,8 @@ void ai_CreateAIMainNUI(object oPC) jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_perception_distance", 2, FALSE, 35.0f, 20.0f, "txt_perception_distance_tooltip"); jGroupRow = CreateLabel(jGroupRow, "meters is the distance a monster can respond to allies.", "lbl_perception_distance", 411.0f, 20.0f, NUI_HALIGN_LEFT, 0, 0.0, "txt_perception_distance_tooltip"); jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - jGroupRow = CreateCheckBox(JsonArray(), " Monsters can prebuff before combat starts.", "chbx_buff_monsters", 450.0, 20.0); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters buff before combat starts.", "chbx_buff_monsters", 275.0, 20.0, "chbx_buff_monsters_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " Will use all buff spells!", "chbx_full_buff", 210.0, 20.0, "chbx_full_buff_tooltip"); jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); jGroupRow = CreateCheckBox(JsonArray(), " Monsters can use summons before combat starts.", "chbx_buff_summons", 450.0, 20.0); jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); @@ -241,11 +451,11 @@ void ai_CreateAIMainNUI(object oPC) float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); // Set the Layout of the window. json jLayout = NuiCol(jCol); - string sName = GetName(oPC); + string sName = ai_StripColorCodes(GetName(oPC)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; int nToken = SetWindow(oPC, jLayout, AI_MAIN_NUI, sName + " PEPS Main Menu", - fX, fY, 534.0f, fHeight, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + fX, fY, 554.0f, fHeight, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); // Save the associate to the nui for use in 0e_nui json jData = JsonArray(); jData = JsonArrayInsert(jData, JsonString(ObjectToString(oPC))); @@ -257,49 +467,45 @@ void ai_CreateAIMainNUI(object oPC) // Row 1 - Version label. // Row 2 int nUsing; - // Check the monster AI. - string sLocation = ResManGetAliasFor("ai_default", RESTYPE_NCS); - if(sLocation != "") + if(!AI_SERVER) { - nUsing = TRUE; - string sLocation = ResManGetAliasFor("nw_c2_default1", RESTYPE_NCS); - if(sLocation != "OVERRIDE:" && sLocation != "PATCH:peps" && sLocation != "DEVELOPMENT:") nUsing = FALSE; - if(nUsing) sText = "Monster AI working"; - else sText = "Monster AI not working"; + // Check the monster AI. + string sLocation = ResManGetAliasFor("ai_default", RESTYPE_NCS); + if(sLocation != "") + { + nUsing = TRUE; + string sLocation = ResManGetAliasFor("nw_c2_default1", RESTYPE_NCS); + if(sLocation != "OVERRIDE:" && sLocation != "PATCH:peps" && sLocation != "DEVELOPMENT:") nUsing = FALSE; + if(nUsing) sText = "Monster AI working"; + else sText = "Monster AI not working"; + } + else sText = "Monster AI not loaded"; + // Check the associate AI. + sLocation = ResManGetAliasFor("ai_a_default", RESTYPE_NCS); + if(sLocation != "") + { + nUsing = TRUE; + string sLocation = ResManGetAliasFor("nw_ch_ac1", RESTYPE_NCS); + if(sLocation != "OVERRIDE:" && sLocation != "PATCH:peps" && sLocation != "DEVELOPMENT:") nUsing = FALSE; + if(nUsing) sText += ", Associate AI working"; + else sText += ", Associate AI not working"; + } + else sText += ", Associate AI not loaded"; + // Check for PRC. + sLocation = ResManGetAliasFor("prc_ai_fam_percp", RESTYPE_NCS); + if(sLocation != "") sText += ", PRC loaded."; + else + { + // Check the player AI. + sLocation = ResManGetAliasFor("xx_pc_1_hb", RESTYPE_NCS); + if(sLocation != "") sText += ", Player AI loaded."; + else sText += ", Player AI not loaded."; + } + NuiSetBind(oPC, nToken, "lbl_ai_info_label", JsonString(sText)); } - else sText = "Monster AI not loaded"; - // Check the associate AI. - sLocation = ResManGetAliasFor("ai_a_default", RESTYPE_NCS); - if(sLocation != "") - { - nUsing = TRUE; - string sLocation = ResManGetAliasFor("nw_ch_ac1", RESTYPE_NCS); - if(sLocation != "OVERRIDE:" && sLocation != "PATCH:peps" && sLocation != "DEVELOPMENT:") nUsing = FALSE; - if(nUsing) sText += ", Associate AI working"; - else sText += ", Associate AI not working"; - } - else sText += ", Associate AI not loaded"; - // Check for PRC. - sLocation = ResManGetAliasFor("prc_ai_fam_percp", RESTYPE_NCS); - if(sLocation != "") sText += ", PRC loaded."; - else - { - // Check the player AI. - sLocation = ResManGetAliasFor("xx_pc_1_hb", RESTYPE_NCS); - if(sLocation != "") sText += ", Player AI loaded."; - else sText += ", Player AI not loaded."; - } - NuiSetBind(oPC, nToken, "lbl_ai_info_label", JsonString(sText)); // Row 3 NuiSetBind(oPC, nToken, "btn_plugin_manager_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_plugin_manager_tooltip", JsonString(" Manages external executable scripts.")); - if(nAssociateAI) - { - NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget_event", JsonBool(TRUE)); - int bWidgetOn = !ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, OBJECT_INVALID, "pc"); - NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget", JsonBool(bWidgetOn)); - NuiSetBind(oPC, nToken, "btn_assoc_widget_tooltip", JsonString(" Turns On/Off all associate widgets.")); - } int bActionGhost = ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST); NuiSetBind(oPC, nToken, "btn_action_ghost", JsonBool (bActionGhost)); NuiSetBind(oPC, nToken, "btn_action_ghost_event", JsonBool(TRUE)); @@ -332,9 +538,15 @@ void ai_CreateAIMainNUI(object oPC) NuiSetBind(oPC, nToken, "txt_ai_difficulty", JsonString(IntToString(GetLocalInt(oModule, AI_RULE_AI_DIFFICULTY)))); NuiSetBindWatch(oPC, nToken, "txt_ai_difficulty", TRUE); NuiSetBind(oPC, nToken, "txt_ai_difficulty_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "chbx_buff_monsters_check", JsonBool(GetLocalInt(oModule, AI_RULE_BUFF_MONSTERS))); + int bMonsterBuff = GetLocalInt(oModule, AI_RULE_BUFF_MONSTERS); + NuiSetBind(oPC, nToken, "chbx_buff_monsters_check", JsonBool(bMonsterBuff)); NuiSetBindWatch(oPC, nToken, "chbx_buff_monsters_check", TRUE); NuiSetBind(oPC, nToken, "chbx_buff_monsters_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_buff_monsters_tooltip", JsonString(" Monsters will cast all longer duration buff spells just before combat starts.")); + NuiSetBind(oPC, nToken, "chbx_full_buff_check", JsonBool(GetLocalInt(oModule, AI_RULE_FULL_BUFF_MONSTERS))); + NuiSetBindWatch(oPC, nToken, "chbx_full_buff_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_full_buff_event", JsonBool(bMonsterBuff)); + NuiSetBind(oPC, nToken, "chbx_full_buff_tooltip", JsonString(" Monsters will cast all buff spells just before combat starts! VERY DIFFICULTY!")); NuiSetBind(oPC, nToken, "chbx_buff_summons_check", JsonBool(GetLocalInt(oModule, AI_RULE_PRESUMMON))); NuiSetBindWatch(oPC, nToken, "chbx_buff_summons_check", TRUE); NuiSetBind(oPC, nToken, "chbx_buff_summons_event", JsonBool(TRUE)); @@ -390,6 +602,7 @@ void ai_CreateAIMainNUI(object oPC) NuiSetBind(oPC, nToken, "txt_inc_enc_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "txt_inc_hp", JsonString(IntToString(GetLocalInt(oModule, AI_INCREASE_MONSTERS_HP)))); NuiSetBindWatch(oPC, nToken, "txt_inc_hp", TRUE); + NuiSetBind(oPC, nToken, "txt_inc_hp_tooltip", JsonString(" Will increase ALL monsters hitpoints by the%. Upto 500% or 6 times the normal health!")); NuiSetBind(oPC, nToken, "txt_inc_hp_event", JsonBool(TRUE)); } if(nMonsterAI || nAssociateAI) @@ -442,13 +655,14 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) { if(bIsPC) { - if(bUsingPCAI || !AI_SERVER) + bLeft = ai_GetIsServer(); + if(bUsingPCAI || !bLeft) { if(bUsingPCAI) { jRow = CreateButton(jRow, "AI Menu", "btn_ai_menu", 232.0, 20.0, -1.0, "btn_ai_menu_tooltip"); } - if(!AI_SERVER) + if(!bLeft) { jRow = CreateButton(jRow, "Main Menu", "btn_main_menu", 232.0, 20.0, -1.0, "btn_main_menu_tooltip"); } @@ -474,6 +688,29 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jRow = CreateButtonSelect(jRow, "Vertical Widget", "btn_vertical_widget", 154.0, 20.0, "btn_vertical_widget_tooltip"); jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 3 ******************************************************************* 500 / 129 + bRight = !ai_GetDMWAccessButton(BTN_ASSOC_WIDGETS_OFF); + //bLeft = !ai_GetDMWAccessButton(BTN_CMD_GUARD); + if(bRight)// || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + if(bIsPC && bUsingHenchAI) + { + jRow = CreateButton(jRow, "Associate Widgets", "btn_toggle_assoc_widget", 200.0f, 20.0f, -1.0, "btn_toggle_assoc_widget_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_toggle_assoc_widget", 25.0, 20.0); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + //if(bLeft) + //{ + //} + //jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + //fHeight += 28.0; + } + // Row 4 ******************************************************************* 500 / 129 bRight = !ai_GetDMWAccessButton(BTN_CMD_ACTION); bLeft = !ai_GetDMWAccessButton(BTN_CMD_GUARD); if(bRight || bLeft) @@ -493,7 +730,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight += 28.0; } - // Row 4 ******************************************************************* 500 / 157 + // Row 5 ******************************************************************* 500 / 157 jRow = JsonArray(); bRight = !ai_GetDMWAccessButton(BTN_CMD_HOLD); bLeft = !ai_GetDMWAccessButton(BTN_CMD_ATTACK); @@ -514,7 +751,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight += 28.0; } - // Row 5 ******************************************************************* 500 / 213 + // Row 6 ******************************************************************* 500 / 213 bRight = !ai_GetDMWAccessButton(BTN_CMD_FOLLOW); bLeft = !ai_GetDMAIAccessButton(BTN_AI_FOLLOW_TARGET); if(bRight || bLeft) @@ -534,7 +771,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight += 28.0; } - // Row 6 ******************************************************************* 500 / 185 + // Row 7 ******************************************************************* 500 / 185 if(bIsPC) { bRight = !ai_GetDMWAccessButton(BTN_CMD_SEARCH); @@ -557,7 +794,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) fHeight = fHeight + 28.0; } } - // Row 7 ******************************************************************* 500 / 241 + // Row 8 ******************************************************************* 500 / 241 bRight = !ai_GetDMWAccessButton(BTN_CMD_AI_SCRIPT); bLeft = !ai_GetDMWAccessButton(BTN_CMD_PLACE_TRAP); if(bRight || bLeft) @@ -578,7 +815,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 8 ******************************************************************* 500 / --- + // Row 9 ******************************************************************* 500 / --- int bMemorize = ai_GetIsSpellCaster(oAssociate); int bSpellbook = ai_GetIsSpellBookRestrictedCaster(oAssociate); bRight = !ai_GetDMWAccessButton(BTN_CMD_SPELL_WIDGET); @@ -614,7 +851,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 9 ******************************************************************* 500 / 269 + // Row 10 ******************************************************************* 500 / 269 bRight = !ai_GetDMWAccessButton(BTN_BUFF_SHORT); bLeft = !ai_GetDMWAccessButton(BTN_BUFF_LONG); if(bRight || bLeft) @@ -635,7 +872,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 10 ******************************************************************* 500 / 297 + // Row 11 ******************************************************************* 500 / 297 bRight = !ai_GetDMWAccessButton(BTN_BUFF_ALL); bLeft = !ai_GetDMWAccessButton(BTN_BUFF_REST); if(bRight || bLeft) @@ -656,7 +893,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 11 ******************************************************************* 500 / 325 + // Row 12 ******************************************************************* 500 / 325 bRight = !ai_GetDMWAccessButton(BTN_CMD_JUMP_TO); bLeft = !ai_GetDMWAccessButton(BTN_CMD_GHOST_MODE); if(bRight || bLeft) @@ -677,7 +914,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 12 ****************************************************************** 500 / 353 + // Row 13 ****************************************************************** 500 / 353 bRight = !ai_GetDMWAccessButton(BTN_CMD_CAMERA); bLeft = !ai_GetDMWAccessButton(BTN_CMD_INVENTORY); if(bRight || bLeft) @@ -698,7 +935,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 13 ******************************************************************* 500 / --- + // Row 14 ******************************************************************* 500 / --- int bFamiliar = GetHasFeat(FEAT_SUMMON_FAMILIAR, oAssociate, TRUE); if(!ai_GetDMWAccessButton(BTN_CMD_FAMILIAR) && bFamiliar) { @@ -709,7 +946,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; - // Row 14 ******************************************************************* 500 / --- + // Row 15 ******************************************************************* 500 / --- jRow = JsonArray(); jRow = CreateCombo(jRow, ai_CreateCompanionJson(oPC, "hen_familiar"), "cmb_familiar", 200.0, 20.0); jRow = CreateCheckBox(jRow, "", "chbx_familiar", 25.0, 20.0); @@ -719,7 +956,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 15 ******************************************************************* 500 / --- + // Row 16 ******************************************************************* 500 / --- int bCompanion = GetHasFeat(FEAT_ANIMAL_COMPANION, oAssociate, TRUE); if(!ai_GetDMWAccessButton(BTN_CMD_COMPANION) && bCompanion) { @@ -730,7 +967,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; - // Row 16 ******************************************************************* 500 / --- + // Row 17 ******************************************************************* 500 / --- jRow = JsonArray(); jRow = CreateCombo(jRow, ai_CreateCompanionJson(oPC, "hen_companion"), "cmb_companion", 200.0, 20.0); jRow = CreateCheckBox(jRow, "", "chbx_companion", 25.0, 20.0); @@ -740,7 +977,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight = fHeight + 28.0; } - // Row 17+ ****************************************************************** 500 / --- + // Row 18+ ****************************************************************** 500 / --- string sAssociateType = ai_GetAssociateType(oPC, oAssociate); json jPCPlugins; if(bIsPC) @@ -779,7 +1016,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) jPlugin = JsonArrayGet(jPCPlugins, ++nIndex); } } - // Row 18 ****************************************************************** 500 / --- + // Row 19+ ****************************************************************** 500 / --- jRow = JsonArray(); jRow = JsonArrayInsert(jRow, NuiSpacer()); jRow = CreateLabel(jRow, "", "lbl_info_1", 475.0, 20.0, NUI_HALIGN_CENTER); @@ -798,7 +1035,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) } // Set the Layout of the window. json jLayout = NuiCol(jCol); - string sName = GetName(oAssociate); + string sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_COMMAND_NUI, sName + " Command Menu", @@ -826,6 +1063,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) int bInventory = ai_GetWidgetButton(oPC, BTN_CMD_INVENTORY, oAssociate, sAssociateType); int bBtnFamiliar = ai_GetWidgetButton(oPC, BTN_CMD_FAMILIAR, oAssociate, sAssociateType); int bBtnCompanion = ai_GetWidgetButton(oPC, BTN_CMD_COMPANION, oAssociate, sAssociateType); + int bAssocWidgetOff = ai_GetWidgetButton(oPC, BTN_ASSOC_WIDGETS_OFF, oAssociate, sAssociateType); int bVertical = ai_GetWidgetButton(oPC, BTN_WIDGET_VERTICAL, oAssociate, sAssociateType); // Save the associate to the nui for use in 0e_nui json jData = JsonArray(); @@ -849,7 +1087,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_copy_settings_event", JsonBool (TRUE)); sText = " Copy AI and command settings for one creature to others."; NuiSetBind(oPC, nToken, "btn_copy_settings_tooltip", JsonString(sText)); - if(!AI_SERVER) + if(!ai_GetIsServer()) { NuiSetBind(oPC, nToken, "btn_main_menu_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_main_menu_tooltip", JsonString(" Module Options")); @@ -895,6 +1133,17 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_vertical_widget_tooltip", JsonString( " " + sName + " widget will display vertically")); // Row 3 + if(bIsPC && bUsingHenchAI) + { + NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget_event", JsonBool(TRUE)); + if(ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc")) sText = " Associate Widgets [Off]"; + else sText = " Associate Widgets [On]"; + NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget_tooltip", JsonString(sText)); + NuiSetBind(oPC, nToken, "chbx_toggle_assoc_widget_check", JsonBool (bAssocWidgetOff)); + NuiSetBindWatch (oPC, nToken, "chbx_toggle_assoc_widget_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_toggle_assoc_widget_event", JsonBool(TRUE)); + } + // Row 4 NuiSetBind(oPC, nToken, "chbx_cmd_action_check", JsonBool (bCmdAction)); NuiSetBindWatch(oPC, nToken, "chbx_cmd_action_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_action_event", JsonBool(TRUE)); @@ -903,7 +1152,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBindWatch (oPC, nToken, "chbx_cmd_guard_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_guard_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_cmd_guard_event", JsonBool (TRUE)); - // Row 4 + // Row 5 NuiSetBind(oPC, nToken, "chbx_cmd_hold_check", JsonBool (bCmdHold)); NuiSetBindWatch (oPC, nToken, "chbx_cmd_hold_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_hold_event", JsonBool(TRUE)); @@ -912,7 +1161,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBindWatch (oPC, nToken, "chbx_cmd_attack_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_attack_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_cmd_attack_event", JsonBool (TRUE)); - // Row 5 + // Row 6 NuiSetBind(oPC, nToken, "chbx_cmd_follow_check", JsonBool (bCmdFollow)); NuiSetBindWatch (oPC, nToken, "chbx_cmd_follow_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_follow_event", JsonBool(TRUE)); @@ -921,7 +1170,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBindWatch (oPC, nToken, "chbx_follow_target_check", TRUE); NuiSetBind(oPC, nToken, "chbx_follow_target_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_follow_target_event", JsonBool (TRUE)); - // Row 6 + // Row 7 if(bIsPC) { NuiSetBind(oPC, nToken, "chbx_cmd_search_check", JsonBool (bCmdSearch)); @@ -974,7 +1223,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) else sTarget = GetName(oPC); } NuiSetBind(oPC, nToken, "btn_follow_target_tooltip", JsonString(" " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]")); - // Row 7 + // Row 8 NuiSetBind(oPC, nToken, "chbx_cmd_ai_script_check", JsonBool (bCmdAIScript)); NuiSetBindWatch (oPC, nToken, "chbx_cmd_ai_script_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_ai_script_event", JsonBool(TRUE)); @@ -1007,7 +1256,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_cmd_place_trap_tooltip", JsonString ( " Place a trap at the location selected")); } - // Row 8 + // Row 9 NuiSetBind(oPC, nToken, "btn_quick_widget_event", JsonBool(TRUE)); NuiSetBind (oPC, nToken, "btn_quick_widget_tooltip", JsonString( " Add/Remove abilities and spells from creatures widget")); @@ -1026,26 +1275,29 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBind (oPC, nToken, "btn_spell_known_tooltip", JsonString( " Change known spell list.")); } - // Row 9 + // Row 10 NuiSetBind(oPC, nToken, "chbx_buff_short_check", JsonBool (bBuffShort)); NuiSetBindWatch (oPC, nToken, "chbx_buff_short_check", TRUE); NuiSetBind(oPC, nToken, "chbx_buff_short_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_buff_short_event", JsonBool (TRUE)); + float fDelay = GetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING); + if(fDelay < 0.1) fDelay = 0.1; + string sDelay = FloatToString(fDelay, 0, 1); NuiSetBind (oPC, nToken, "btn_buff_short_tooltip", JsonString ( - " Buff the party with short duration spells")); + " Buff the party with short duration spells. Cast speed [" + sDelay + "]")); NuiSetBind(oPC, nToken, "chbx_buff_long_check", JsonBool (bBuffLong)); NuiSetBindWatch (oPC, nToken, "chbx_buff_long_check", TRUE); NuiSetBind(oPC, nToken, "chbx_buff_long_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_buff_long_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_buff_long_tooltip", JsonString ( - " Buff the party with long duration spells")); - // Row 10 + " Buff the party with long duration spells. Cast speed [" + sDelay + "]")); + // Row 11 NuiSetBind(oPC, nToken, "chbx_buff_all_check", JsonBool (bBuffAll)); NuiSetBindWatch (oPC, nToken, "chbx_buff_all_check", TRUE); NuiSetBind(oPC, nToken, "chbx_buff_all_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_buff_all_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_buff_all_tooltip", JsonString ( - " Buff the party with all our defensive spells")); + " Buff the party with all spells. Cast speed [" + sDelay + "]")); if(!bIsPC && ResManGetAliasFor("prc_ai_fam_percp", RESTYPE_NCS) == "") { NuiSetBind(oPC, nToken, "chbx_buff_rest_check", JsonBool (bBuffRest)); @@ -1056,7 +1308,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) else sText = " [Off] Turn buffing after resting on"; NuiSetBind (oPC, nToken, "btn_buff_rest_tooltip", JsonString (sText)); } - // Row 11 + // Row 12 NuiSetBind(oPC, nToken, "chbx_jump_to_check", JsonBool(bJumpTo)); NuiSetBindWatch (oPC, nToken, "chbx_jump_to_check", TRUE); NuiSetBind(oPC, nToken, "chbx_jump_to_event", JsonBool(TRUE)); @@ -1076,7 +1328,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) if(ai_GetAIMode(oAssociate, AI_MODE_GHOST)) sText = "Off"; NuiSetBind(oPC, nToken, "btn_ghost_mode_tooltip", JsonString ( " Turn " + sText + " clipping through creatures for " + GetName(oAssociate))); - // Row 12 + // Row 13 NuiSetBind(oPC, nToken, "chbx_camera_check", JsonBool (bCamera)); NuiSetBindWatch (oPC, nToken, "chbx_camera_check", TRUE); NuiSetBind(oPC, nToken, "chbx_camera_event", JsonBool(TRUE)); @@ -1089,7 +1341,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_inventory_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_inventory_tooltip", JsonString ( " Open " + sName + " inventory")); - // Row 13 & 14 + // Row 14 & 15 if(bFamiliar) { NuiSetBind(oPC, nToken, "chbx_familiar_check", JsonBool(bBtnFamiliar)); @@ -1115,7 +1367,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "lbl_familiar_name_label", JsonString("Familiar name")); } } - // Row 15 & 16 + // Row 16 & 17 if(bCompanion) { NuiSetBind(oPC, nToken, "chbx_companion_check", JsonBool(bBtnCompanion)); @@ -1143,7 +1395,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) } if(bIsPC) { - // Row 17+ + // Row 18+ int nIndex, bWidget; string sButton, sText; json jPlugin = JsonArrayGet(jPCPlugins, nIndex); @@ -1164,7 +1416,7 @@ void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) } NuiSetBind(oPC, nToken, "chbx_plugin_tooltip", JsonString(" Adds the plugin to your widget.")); } - // Row 18 + // Row 19+ sText = ai_GetRandomTip(); NuiSetBind(oPC, nToken, "lbl_info_1_label", JsonString(sText)); } @@ -1174,6 +1426,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); int bRight, bLeft; + int nAssociateType = GetAssociateType(oAssociate); float fHeight = 45.0; // ************************************************************************* Width / Height int bIsPC = ai_GetIsCharacter(oAssociate); @@ -1185,7 +1438,8 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) if(bIsPC) { bRight = GetLocalInt(GetModule(), sDMWidgetAccessVarname) != 7340028; - if(!AI_SERVER || bRight) + bLeft = ai_GetIsServer(); + if(!bLeft || bRight) { // If all the Command buttons are blocked then don't load the menu. if(bRight) @@ -1194,10 +1448,10 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) jRow = CreateLabel(jRow, "", "blank_label_2", 25.0, 20.0); } jRow = JsonArrayInsert(jRow, NuiSpacer()); - if(!AI_SERVER) + if(!bLeft) { - CreateButton(jRow, "Main Menu", "btn_main_menu", 200.0, 20.0, -1.0, "btn_main_menu_tooltip"); - CreateLabel(jRow, "", "blank_label_2", 25.0, 20.0); + jRow = CreateButton(jRow, "Main Menu", "btn_main_menu", 200.0, 20.0, -1.0, "btn_main_menu_tooltip"); + jRow = CreateLabel(jRow, "", "blank_label_2", 25.0, 20.0); } jCol = JsonArrayInsert(jCol, NuiRow(jRow)); fHeight += 28.0; @@ -1223,7 +1477,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) fHeight += 28.0; } // Row 3 ******************************************************************* 500 / 101 - bRight = !ai_GetDMAIAccessButton(BTN_AI_FOR_PC); + bRight = TRUE;//!ai_GetDMAIAccessButton(BTN_AI_FOR_PC); bLeft = !ai_GetDMAIAccessButton(BTN_AI_REDUCE_SPEECH); if(bRight || bLeft) { @@ -1436,7 +1690,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) jRow = JsonArrayInsert(jRow, NuiSpacer()); if(bLeft) { - if(sAssociateType != "summons" && sAssociateType != "dominated") + if(nAssociateType != ASSOCIATE_TYPE_SUMMONED && nAssociateType != ASSOCIATE_TYPE_DOMINATED) { jRow = CreateButton(jRow, "Auto Looting", "btn_loot", 200.0, 20.0, -1.0, "btn_loot_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_loot", 25.0, 20.0); @@ -1516,7 +1770,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) } // Set the Layout of the window. json jLayout = NuiCol(jCol); - string sText, sName = GetName(oAssociate); + string sText, sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_NUI, sName + " AI Menu", @@ -1559,7 +1813,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) if(bIsPC) { bRight = GetLocalInt(GetModule(), sDMWidgetAccessVarname) != 7340028; - if(!AI_SERVER || bRight) + if(!ai_GetIsServer() || bRight) { // If all the Command buttons are blocked then don't load the menu. if(bRight) @@ -1567,7 +1821,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_command_menu_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_command_menu_tooltip", JsonString(" " + sName + " Command options")); } - if(!AI_SERVER) + if(!ai_GetIsServer()) { NuiSetBind(oPC, nToken, "btn_main_menu_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_main_menu_tooltip", JsonString(" Module Options")); @@ -1585,7 +1839,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_loot_filter", JsonInt(TRUE)); // Row 3 // Only activate ai on/off if this is for the pc. - if(bIsPC && ResManGetAliasFor("prc_ai_fam_percp", RESTYPE_NCS) == "") + if(bIsPC && TRUE)//ResManGetAliasFor("prc_ai_fam_percp", RESTYPE_NCS) == "") { NuiSetBind(oPC, nToken, "chbx_ai_check", JsonBool(bAI)); NuiSetBindWatch (oPC, nToken, "chbx_ai_check", TRUE); @@ -1751,7 +2005,7 @@ void ai_CreateAssociateAINUI(object oPC, object oAssociate) if(ai_GetMagicMode(oAssociate, AI_MAGIC_CURE_SPELLS_OFF)) sText = " Cast Cure Spells Off"; else sText = " Cast Cure Spells On"; NuiSetBind(oPC, nToken, "btn_cure_onoff_tooltip", JsonString(sText)); - if(sAssociateType != "summons" && sAssociateType != "dominated") + if(nAssociateType != ASSOCIATE_TYPE_SUMMONED && nAssociateType != ASSOCIATE_TYPE_DOMINATED) { sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE), 0, 0); if(ai_GetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS)) sText = " Looting On [" + sRange + " meters]"; @@ -1817,11 +2071,30 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int // Set event watches for save window location. NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); // Set the buttons to show events. - NuiSetBind(oPC, nToken, "btn_open_main_image", JsonString(GetPortraitResRef(oAssociate) + "s")); + string sPortrait = GetPortraitResRef(oAssociate); + string sSize; + if(ResManGetAliasFor(sPortrait + "s", RESTYPE_TGA) != "") sSize = "s"; + else if(ResManGetAliasFor(sPortrait + "m", RESTYPE_TGA) != "") sSize = "m"; + else if(ResManGetAliasFor(sPortrait + "l", RESTYPE_TGA)!= "") sSize = "l"; + else if(ResManGetAliasFor(sPortrait + "h", RESTYPE_TGA)!= "") sSize = "h"; + else sSize = "m"; + NuiSetBind(oPC, nToken, "btn_open_main_image", JsonString(sPortrait + sSize)); NuiSetBind(oPC, nToken, "btn_open_main_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_open_main_tooltip", JsonString(" " + sName + " widget menu")); + if(ai_GetWidgetButton(oPC, BTN_ASSOC_WIDGETS_OFF, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget_event", JsonBool(TRUE)); + if(ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oPC, "pc")) sText = " Associate Widgets [Off]"; + else sText = " Associate Widgets [On]"; + NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget_tooltip", JsonString(sText)); + } if(bIsPC) sText = " All associates"; else sText = " " + GetName(oAssociate); + if(ai_GetWidgetButton(oPC, BTN_CMD_CAMERA, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_camera_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_camera_tooltip", JsonString(" Toggle camera view for " + sName)); + } if(ai_GetWidgetButton(oPC, BTN_CMD_ACTION, oAssociate, sAssociateType)) { NuiSetBind(oPC, nToken, "btn_cmd_action_event", JsonBool(TRUE)); @@ -1960,17 +2233,29 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int if(ai_GetWidgetButton(oPC, BTN_BUFF_SHORT, oAssociate, sAssociateType)) { NuiSetBind(oPC, nToken, "btn_buff_short_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_buff_short_tooltip", JsonString(" Buff the party with short duration spells")); + float fDelay = GetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING); + if(fDelay < 0.1) fDelay = 0.1; + string sDelay = FloatToString(fDelay, 0, 1); + NuiSetBind (oPC, nToken, "btn_buff_short_tooltip", JsonString( + " Buff the party with short duration spells. Cast speed [" + sDelay + "]")); } if(ai_GetWidgetButton(oPC, BTN_BUFF_LONG, oAssociate, sAssociateType)) { NuiSetBind(oPC, nToken, "btn_buff_long_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_buff_long_tooltip", JsonString(" Buff the party with long duration spells")); + float fDelay = GetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING); + if(fDelay < 0.1) fDelay = 0.1; + string sDelay = FloatToString(fDelay, 0, 1); + NuiSetBind (oPC, nToken, "btn_buff_long_tooltip", JsonString( + " Buff the party with long duration spells. Cast speed [" + sDelay + "]")); } if(ai_GetWidgetButton(oPC, BTN_BUFF_ALL, oAssociate, sAssociateType)) { NuiSetBind(oPC, nToken, "btn_buff_all_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_buff_all_tooltip", JsonString(" Buff the party with all our defensive spells")); + float fDelay = GetLocalFloat(oAssociate, AI_DELAY_BUFF_CASTING); + if(fDelay < 0.1) fDelay = 0.1; + string sDelay = FloatToString(fDelay, 0, 1); + NuiSetBind (oPC, nToken, "btn_buff_all_tooltip", JsonString( + " Buff the party with all spells. Cast speed [" + sDelay + "]")); } if(ai_GetWidgetButton(oPC, BTN_CMD_JUMP_TO, oAssociate, sAssociateType)) { @@ -1988,11 +2273,6 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int NuiSetBind(oPC, nToken, "btn_ghost_mode_tooltip", JsonString ( " Turn " + sText + " clipping through creatures for " + GetName(oAssociate))); } - if(ai_GetWidgetButton(oPC, BTN_CMD_CAMERA, oAssociate, sAssociateType)) - { - NuiSetBind(oPC, nToken, "btn_camera_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_camera_tooltip", JsonString(" Toggle camera view for " + sName)); - } if(ai_GetWidgetButton(oPC, BTN_CMD_INVENTORY, oAssociate, sAssociateType)) { NuiSetBind(oPC, nToken, "btn_inventory_event", JsonBool(TRUE)); @@ -2238,7 +2518,7 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int object oItem; if(JsonGetType(jWidget) != JSON_TYPE_NULL) { - int nLevel, nSpell, nIndex, nClass, nMetaMagic, nDomain, nSubSpell, nFeat; + int nLevel, nSpell, nIndex, nClass, nMetaMagic, nDomain, nSubSpell, nFeat, nSAIndex, nUses; string sSpellIcon, sMetaMagicText, sSubSpell, sClass, sIndex; while(nIndex < 10) { @@ -2283,9 +2563,10 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int if(nUses) { NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); - if(nUses == 999) sText = "Unlimited"; + if(nUses == 999) sText = ""; else sText = IntToString(nUses); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + " / " + sText + ")")); + NuiSetBind(oPC, nToken, "uses_" + sIndex + "_text", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + ")")); } else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); } @@ -2308,8 +2589,10 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int } NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - if(GetHasFeat(nFeat, oAssociate)) + nUses = GetHasFeat(nFeat, oAssociate); + if(nUses > 0) { + NuiSetBind(oPC, nToken, "uses_" + sIndex + "_text", JsonString(IntToString(nUses))); NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); } else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); @@ -2319,20 +2602,35 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); - nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); + nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nMetaMagic, nDomain); NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); - if(GetSpellUsesLeft(oAssociate, nClass, nSpell, nMetaMagic, nDomain)) + nSAIndex = JsonGetInt(JsonArrayGet(jSpell, 6)); + if(nClass == 255) { - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); + if(GetSpellAbilityReady(oAssociate, nSAIndex)) + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (Special Ability / " + IntToString(nLevel) + ")")); + } + else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + else + { + nUses = GetSpellUsesLeft(oAssociate, nClass, nSpell, nMetaMagic, nDomain); + if(nUses > 0) + { + NuiSetBind(oPC, nToken, "uses_" + sIndex + "_text", JsonString(IntToString(nUses))); + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); + } + else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); } - else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); } } } @@ -2385,9 +2683,10 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int if(nUses) { NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); - if(nUses == 999) sText = "Unlimited"; + if(nUses == 999) sText = ""; else sText = IntToString(nUses); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + " / " + sText + ")")); + NuiSetBind(oPC, nToken, "uses_" + sIndex + "_text", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + ")")); } else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); } @@ -2409,8 +2708,10 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int } NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - if(GetHasFeat(nFeat, oAssociate)) + nUses = GetHasFeat(nFeat, oAssociate); + if(nUses > 0) { + NuiSetBind(oPC, nToken, "uses_" + sIndex + "_text", JsonString(IntToString(nUses))); NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); } else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); @@ -2419,8 +2720,8 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int { nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); - nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); + nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); //SendMessageToPC(oPC, GetName(oAssociate) + " nSpell: " + IntToString(nSpell) + // " nClass: " + IntToString(nClass) + " nMetaMagic: " + IntToString(nMetaMagic) + @@ -2431,17 +2732,28 @@ void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); sSubSpell = Get2DAString("spells", "Master", nSpell); if(sSubSpell != "") nSpell = StringToInt(sSubSpell); - if(nDomain == -1 || GetSpellUsesLeft(oAssociate, nClass, nSpell, nMetaMagic, nDomain)) + if(nClass == 255) { - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - if(nDomain == -1) NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); - else + nSAIndex = JsonGetInt(JsonArrayGet(jSpell, 6)); + if(GetSpellAbilityReady(oAssociate, nSAIndex)) { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (Special Ability / " + IntToString(nLevel) + ")")); + } + else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + else + { + nUses = GetSpellUsesLeft(oAssociate, nClass, nSpell, nMetaMagic, nDomain); + if(nUses > 0) + { + NuiSetBind(oPC, nToken, "uses_" + sIndex + "_text", JsonString(IntToString(nUses))); + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); } + else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); } - else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); } } else break; @@ -2468,8 +2780,30 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) jButton = NuiHeight(jButton, 35.0); jButton = NuiMargin(jButton, 0.0); jButton = NuiTooltip(jButton, NuiBind ("btn_open_main_tooltip")); - jButton = NuiImageRegion(jButton, NuiRect(0.0, 0.0, 32.0, 35.0)); + string sPortrait = GetPortraitResRef(oAssociate); + if(ResManGetAliasFor(sPortrait + "s", RESTYPE_TGA) != "") + jButton = NuiImageRegion(jButton, NuiRect(0.0, 0.0, 32.0, 50.0)); + else if(ResManGetAliasFor(sPortrait + "m", RESTYPE_TGA) != "") + jButton = NuiImageRegion(jButton, NuiRect(0.0, 0.0, 64.0, 100.0)); + else if(ResManGetAliasFor(sPortrait + "l", RESTYPE_TGA)!= "") + jButton = NuiImageRegion(jButton, NuiRect(0.0, 0.0, 128.0, 200.0)); + else if(ResManGetAliasFor(sPortrait + "h", RESTYPE_TGA)!= "") + jButton = NuiImageRegion(jButton, NuiRect(0.0, 0.0, 256.0, 400.0)); + else jButton = NuiImageRegion(jButton, NuiRect(0.0, 0.0, 64.0, 100.0)); + jButton = NuiAspect(jButton, 1.0); + //jButton = NuiImageRegion(jButton, NuiRect(0.0, 0.0, 32.0, 35.0)); + //jButton = NuiImage(jButton, JsonInt(NUI_ASPECT_FIT100), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_TOP)); json jRow = JsonArrayInsert(JsonArray(), jButton); + if(ai_GetWidgetButton(oPC, BTN_ASSOC_WIDGETS_OFF, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_invite", "btn_toggle_assoc_widget", 35.0f, 35.0f, 0.0, "btn_toggle_assoc_widget_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_CAMERA, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_examine", "btn_camera", 35.0f, 35.0f, 0.0, "btn_camera_tooltip"); + fButtons += 1.0; + } if(ai_GetWidgetButton(oPC, BTN_CMD_ACTION, oAssociate, sAssociateType)) { jRow = CreateButtonImage(jRow, "ir_action", "btn_cmd_action", 35.0f, 35.0f, 0.0, "btn_cmd_action_tooltip"); @@ -2553,11 +2887,6 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) jRow = CreateButtonImage(jRow, "dm_limbo", "btn_ghost_mode", 35.0f, 35.0f, 0.0, "btn_ghost_mode_tooltip"); fButtons += 1.0; } - if(ai_GetWidgetButton(oPC, BTN_CMD_CAMERA, oAssociate, sAssociateType)) - { - jRow = CreateButtonImage(jRow, "ir_examine", "btn_camera", 35.0f, 35.0f, 0.0, "btn_camera_tooltip"); - fButtons += 1.0; - } if(ai_GetWidgetButton(oPC, BTN_CMD_INVENTORY, oAssociate, sAssociateType)) { jRow = CreateButtonImage(jRow, "ir_pickup", "btn_inventory", 35.0f, 35.0f, 0.0, "btn_inventory_tooltip"); @@ -2754,7 +3083,7 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) fQuickWidgetColumns += 1.0; int bAdd; float fSpellButtons; - json jButton, jRectangle, jMetaMagic, jDrawList; + json jButton, jRectangle, jMetaMagic, jDrawList, jUses; // Add row to the column. if(bVertical) jCol = JsonArrayInsert(jCol, NuiCol(jRow)); else jCol = JsonArrayInsert(jCol, NuiRow(jRow)); @@ -2787,10 +3116,16 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) jButton = NuiWidth(NuiHeight(jButton, 35.0), 35.0); jButton = NuiMargin(jButton, 0.0); jButton = NuiTooltip(jButton, NuiBind("btn_widget_" + sIndex + "_tooltip")); - jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + // Metamagic text. + jRectangle = NuiRect(4.0, 24.0, 10.0, 31.0); jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + // Spell uses text. + jRectangle = NuiRect(24.0, 2.0, 31.0, 8.0); + jUses = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 255), jRectangle, NuiBind("uses_" + sIndex + "_text")); + jDrawList = JsonArrayInsert(JsonArray(), jUses); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); jRow = JsonArrayInsert(jRow, jButton); fSpellButtons += 1.0; } @@ -2833,10 +3168,16 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) jButton = NuiWidth(NuiHeight(jButton, 35.0), 35.0); jButton = NuiMargin(jButton, 0.0); jButton = NuiTooltip(jButton, NuiBind("btn_widget_" + sIndex + "_tooltip")); - jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + // Metamagic text. + jRectangle = NuiRect(4.0, 24.0, 10.0, 31.0); jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + // Spell uses text. + jRectangle = NuiRect(24.0, 2.0, 31.0, 8.0); + jUses = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 255), jRectangle, NuiBind("uses_" + sIndex + "_text")); + jDrawList = JsonArrayInsert(JsonArray(), jUses); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); jRow = JsonArrayInsert(jRow, jButton); fSpellButtons += 1.0; } @@ -2876,7 +3217,6 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) } // Get the window location to restore it from the database. json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); - //SendMessageToPC(oPC, "0i_menu, 2124, sAssociateType: " + sAssociateType + " jLocations: " + JsonDump(jLocations, 1)); if(JsonGetType(jLocations) == JSON_TYPE_NULL) { ai_SetupAssociateData(oPC, oAssociate, sAssociateType); @@ -2885,14 +3225,16 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) jLocations = JsonObjectGet(jLocations, sAssociateType + AI_WIDGET_NUI); float fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + //SendMessageToPC(oPC, "0i_menu, 2901, sAssociateType: " + sAssociateType + AI_WIDGET_NUI + " jLocations: " + JsonDump(jLocations, 1)); // Keeps the widgets from bunching up in the top corner. if(fY == 0.0 && fX == 0.0) { + int nAssociateType = GetAssociateType(oAssociate); if(sAssociateType == "pc") fY = 1.0; - else if(sAssociateType == "familiar") fY = 96.0 * fScale; - else if(sAssociateType == "companion") fY = 192.0 * fScale; - else if(sAssociateType == "summons") fY = 288.0 * fScale; - else if(sAssociateType == "dominated") fY = 384.0 * fScale; + else if(nAssociateType == ASSOCIATE_TYPE_FAMILIAR) fY = 96.0 * fScale; + else if(nAssociateType == ASSOCIATE_TYPE_ANIMALCOMPANION) fY = 192.0 * fScale; + else if(nAssociateType == ASSOCIATE_TYPE_SUMMONED) fY = 288.0 * fScale; + else if(nAssociateType == ASSOCIATE_TYPE_DOMINATED) fY = 384.0 * fScale; else { int nIndex = 1; @@ -2929,7 +3271,7 @@ void ai_CreateWidgetNUI(object oPC, object oAssociate) json jLayout; int nToken, bBool; string sHeal, sText, sRange; - string sName = GetName(oAssociate); + string sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; if(bVertical) @@ -3074,7 +3416,7 @@ void ai_CreateLootFilterNUI(object oPC, object oAssociate) } // Set the Layout of the window. json jLayout = NuiCol(jCol); - string sText, sName = GetName(oAssociate); + string sText, sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_LOOTFILTER_NUI, sName + " Loot Filter", @@ -3152,7 +3494,7 @@ void ai_CreateCopySettingsNUI(object oPC, object oAssociate) DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); // ************************************************************************* Width / Height // Row 1 ******************************************************************* 244 / 73 - string sName = GetName(oAssociate); + string sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); @@ -3178,24 +3520,34 @@ void ai_CreateCopySettingsNUI(object oPC, object oAssociate) jRow = JsonArrayInsert(jRow, NuiSpacer()); // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 4 ******************************************************************* 244 / 185 - jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); - jRow = CreateButton(jRow, "Summons", "btn_paste_summons", 220.0, 20.0); - jRow = JsonArrayInsert(jRow, NuiSpacer()); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 5 ******************************************************************* 244 / 213 + // Row 4 ******************************************************************* 244 / 213 jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); jRow = CreateButton(jRow, "Dominated", "btn_paste_dominated", 220.0, 20.0); jRow = JsonArrayInsert(jRow, NuiSpacer()); // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 5+ ****************************************************************** 244 / 241 - float fHeight = 241.0; + // Row 5+ ******************************************************************* 244 / 185 + float fHeight = 213.0; int nIndex; string sAssocName; object oAssoc; for(nIndex = 1; nIndex < AI_MAX_HENCHMAN; nIndex++) + { + oAssoc = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, nIndex); + if(oAssoc != OBJECT_INVALID) + { + sAssocName = GetName(oAssoc); + if(GetStringRight(sAssocName, 1) == "s") sAssocName = sAssocName + "'"; + else sAssocName = sAssocName + "'s"; + jRow = CreateButton(JsonArray(), sAssocName, "btn_paste_summons" + IntToString(nIndex), 220.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + else break; + } + // Row 5+ ****************************************************************** 244 / 241 + for(nIndex = 1; nIndex < AI_MAX_HENCHMAN; nIndex++) { oAssoc = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); if(oAssoc != OBJECT_INVALID) @@ -3309,7 +3661,7 @@ void ai_CreatePluginNUI(object oPC) } // Set the Layout of the window. json jLayout = NuiCol(jCol); - sName = GetName(oPC); + sName = ai_StripColorCodes(GetName(oPC)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; int nToken = SetWindow(oPC, jLayout, AI_PLUGIN_NUI, sName + " PEPS Plugin Manager", @@ -3436,7 +3788,7 @@ json ai_CheckItemAbilities(json jQuickListArray, object oCreature, object oItem, if(nCharges) nUses = nCharges; else nUses = nPerDay; } - sSpellIcon = Get2DAString("spells", "iConResRef", nSpell); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); } jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); @@ -3584,7 +3936,7 @@ void ai_CreateQuickWidgetSelectionNUI(object oPC, object oAssociate) } // Set the Layout of the window. json jLayout = NuiCol(jCol); - string sText, sName = GetName(oAssociate); + string sText, sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_QUICK_WIDGET_NUI, sName + " Quick Widget Menu", @@ -3661,7 +4013,7 @@ void ai_CreateQuickWidgetSelectionNUI(object oPC, object oAssociate) { if(StringToInt(Get2DAString("classes", "SpellCaster", nClass))) { - int nClassLevel = GetLevelByClass(nClass, oAssociate); + int nClassLevel = ai_GetCasterTotalLevel(oAssociate, nClass); string sSpellsGained = Get2DAString("classes", "SpellGainTable", nClass); int nMaxSpellLevel = StringToInt(Get2DAString(sSpellsGained, "NumSpellLevels", nClassLevel - 1)); for(nLevelIndex = 0; nLevelIndex <= 9; nLevelIndex++) @@ -3741,8 +4093,8 @@ void ai_CreateQuickWidgetSelectionNUI(object oPC, object oAssociate) jQuickListArray = ai_CheckItemAbilities(jQuickListArray, oAssociate, oItem, jSpell_Icon, jSpell_Text, FALSE); jSpell_Icon = GetLocalJson(oAssociate, "JSPELL_ICON"); jSpell_Text = GetLocalJson(oAssociate, "JSPELL_NAME"); - WriteTimestampedLogEntry("0i_menus, 3643, oAssociate: " + GetName(oAssociate) + - " jSpell_Text: " + JsonDump(jSpell_Text, 4)); + //WriteTimestampedLogEntry("0i_menus, 3643, oAssociate: " + GetName(oAssociate) + + // " jSpell_Text: " + JsonDump(jSpell_Text, 4)); } } oItem = GetNextItemInInventory(oAssociate); @@ -3773,72 +4125,130 @@ void ai_CreateQuickWidgetSelectionNUI(object oPC, object oAssociate) // Special abilities and skills. else if(nLevel == 10) { - for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + json jCreature = ObjectToJson(oAssociate); + json jFeatList = GffGetList(jCreature, "FeatList"); + int nIndex, nSuccessor; + json jFeat = JsonArrayGet(jFeatList, nIndex); + while(JsonGetType(jFeat) != JSON_TYPE_NULL) { - nClassIndex = GetClassByPosition(nIndex, oAssociate); - if(nClassIndex != CLASS_TYPE_INVALID) + nFeat = JsonGetInt(GffGetWord(jFeat, "Feat")); + if(Get2DAString("feat", "USESPERDAY", nFeat) != "" || + Get2DAString("feat", "HostileFeat", nFeat) != "") { - nCounter = 0; - sClassFeats = Get2DAString("classes", "FeatsTable", nClassIndex); - nMax2daRow = Get2DARowCount(sClassFeats); - while(nCounter < nMax2daRow) + // Check for subfeats. + nSpell = StringToInt(Get2DAString("feat", "SPELLID", nFeat)); + nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell1", nSpell)); + //SendMessageToPC(oPC, "nFeat: " + IntToString(nFeat) + + // " nSpell: " + IntToString(nSpell) + + // " nSubSpell: " + IntToString(nSubSpell)); + if(nSubSpell) { - if(Get2DAString(sClassFeats, "OnMenu", nCounter) != "0") + for(nSubSpellIndex = 1; nSubSpellIndex <= 5; nSubSpellIndex++) { - nFeat = StringToInt(Get2DAString(sClassFeats, "FeatIndex", nCounter)); - if(GetHasFeat(nFeat, oAssociate, TRUE)) + sSubSpellIndex = IntToString(nSubSpellIndex); + nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell" + sSubSpellIndex, nSpell)); + //SendMessageToPC(oPC, " nSpell: " + IntToString(nSpell) + + // " nSubSpell: " + IntToString(nSubSpell)); + if(nSubSpell != 0) { - // Check for subfeats. - nSpell = StringToInt(Get2DAString("feat", "SPELLID", nFeat)); - nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell1", nSpell)); - //SendMessageToPC(oPC, "nFeat: " + IntToString(nFeat) + - // " nSpell: " + IntToString(nSpell) + - // " nSubSpell: " + IntToString(nSubSpell)); - if(nSubSpell) - { - for(nSubSpellIndex = 1; nSubSpellIndex <= 5; nSubSpellIndex++) - { - sSubSpellIndex = IntToString(nSubSpellIndex); - nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell" + sSubSpellIndex, nSpell)); - //SendMessageToPC(oPC, " nSpell: " + IntToString(nSpell) + - // " nSubSpell: " + IntToString(nSubSpell)); - if(nSubSpell != 0) - { - sSpellIcon = Get2DAString("spells", "iConResRef", nSubSpell); - jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); - sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSubSpell))); - jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); - jSpell = JsonArray(); - jSpell = JsonArrayInsert(jSpell, JsonInt(nSubSpell)); - jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); - jSpell = JsonArrayInsert(jSpell, JsonInt(0)); - jSpell = JsonArrayInsert(jSpell, JsonInt(255)); - jSpell = JsonArrayInsert(jSpell, JsonInt(0)); - jSpell = JsonArrayInsert(jSpell, JsonInt(nFeat)); - jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); - } - } - } - else if((nFeat < 71 || nFeat > 81)) - { - sSpellIcon = Get2DAString("feat", "ICON", nFeat); - jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); - sSpellName = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))); - jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); - jSpell = JsonArray(); - jSpell = JsonArrayInsert(jSpell, JsonInt(nSpell)); - jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); - jSpell = JsonArrayInsert(jSpell, JsonInt(0)); - jSpell = JsonArrayInsert(jSpell, JsonInt(255)); - jSpell = JsonArrayInsert(jSpell, JsonInt(0)); - jSpell = JsonArrayInsert(jSpell, JsonInt(nFeat)); - jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); - } + sSpellIcon = Get2DAString("spells", "iConResRef", nSubSpell); + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSubSpell))); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSubSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); + jSpell = JsonArrayInsert(jSpell, JsonInt(-1)); // Level + jSpell = JsonArrayInsert(jSpell, JsonInt(255)); // MetaMagic + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Domain + jSpell = JsonArrayInsert(jSpell, JsonInt(nFeat)); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); } } - nCounter++; + } + else if((nFeat < 71 || nFeat > 81)) + { + nSuccessor = StringToInt(Get2DAString("feat", "SUCCESSOR", nFeat)); + if(nSuccessor && GetHasFeat(nSuccessor, oAssociate, TRUE)) + { /* Don't do anything we just skip adding this feat. */} + else + { + sSpellIcon = Get2DAString("feat", "ICON", nFeat); + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Level + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // MetaMagic + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Domain + jSpell = JsonArrayInsert(jSpell, JsonInt(nFeat)); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + } } } + jFeat = JsonArrayGet(jFeatList, ++nIndex); + } + // Checks for monsters special abilities. + int nCounter = 0, nPreviousSpell = -1, nMaxSpellAbility = GetSpellAbilityCount(oAssociate); + while(nCounter < nMaxSpellAbility) + { + nSpell = GetSpellAbilitySpell(oAssociate, nCounter); + if(nPreviousSpell != nSpell) + { + nPreviousSpell = nSpell; + // Check for subfeats. + nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell1", nSpell)); + if(nSubSpell) + { + for(nSubSpellIndex = 1; nSubSpellIndex <= 5; nSubSpellIndex++) + { + sSubSpellIndex = IntToString(nSubSpellIndex); + nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell" + sSubSpellIndex, nSpell)); + if(nSubSpell != 0) + { + sSpellIcon = Get2DAString("spells", "iConResRef", nSubSpell); + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSubSpell))); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSubSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Level + jSpell = JsonArrayInsert(jSpell, JsonInt(255)); // MetaMagic + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Domain + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Feat + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + } + } + } + else + { + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nMetaMagic, nDomain); + jMetaMagic_Text = JsonArrayInsert(jMetaMagic_Text, JsonString(sMetaMagicText)); + jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(255)); // Class - Special abilities is always 255. + jSpell = JsonArrayInsert(jSpell, JsonInt(GetSpellAbilityCasterLevel(oAssociate, nCounter))); + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // metamagic + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // domain + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // feat + // Index of Special ability on monster. + jSpell = JsonArrayInsert(jSpell, JsonInt(nCounter)); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + //SendMessageToPC(oPC, "nSpell: " + IntToString(nSpell) + + // " sSpellIcon: " + sSpellIcon + + // " sSpellName: " + sSpellName+ + // " nMaxSlot: " + IntToString(nMaxSpellAbility) + + // " nSpellAbilityIndex: " + IntToString(nCounter)); + } + } + nCounter++; } // Used in the execution script to get the special abilities. //jData = JsonArrayInsert(jData, jQuickListArray); @@ -3944,185 +4354,7 @@ void ai_CreateQuickWidgetSelectionNUI(object oPC, object oAssociate) NuiSetUserData(oPC, nToken, jData); // Row 4 Quick widget list label. // Row 5 Quick widget List 1 - json jWidget = JsonArrayGet(jSpells, 2); - nIndex = 0; - while(nIndex < 10) - { - jSpell = JsonArrayGet(jWidget, nIndex); - sIndex = IntToString(nIndex); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); - if(JsonGetType(jSpell) != JSON_TYPE_NULL) - { - nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); - nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); - nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); - if(nClass == -1) // This is an Item. - { - string sBaseName; - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - int nBaseItemType = JsonGetInt(JsonArrayGet(jSpell, 3)); - int nIprpSubType = JsonGetInt(JsonArrayGet(jSpell, 4)); - if(nSpell == SPELL_HEALINGKIT) - { - sName = "Healer's Kit +" + IntToString(nIprpSubType); - sSpellIcon = "isk_heal"; - sBaseName = "Healer's Kit"; - } - else if(nBaseItemType == BASE_ITEM_ENCHANTED_SCROLL || - nBaseItemType == BASE_ITEM_SCROLL || - nBaseItemType == BASE_ITEM_SPELLSCROLL) - { - sSpellIcon = Get2DAString("iprp_spells", "Icon", nIprpSubType); - sBaseName = "Scroll"; - } - else - { - if(nBaseItemType == BASE_ITEM_ENCHANTED_POTION || - nBaseItemType == BASE_ITEM_POTIONS) sBaseName = "Potion"; - else if(nBaseItemType == BASE_ITEM_ENCHANTED_WAND || - nBaseItemType == BASE_ITEM_MAGICWAND || - nBaseItemType == FEAT_CRAFT_WAND) sBaseName = "Wand"; - else sBaseName = ai_StripColorCodes(GetName(GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))))); - sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - } - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); - int nUses = ai_GetItemUses(oItem, nIprpSubType); - if(nUses) - { - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); - if(nUses == 999) sText = "Unlimited"; - else sText = IntToString(nUses); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + " / " + sText + ")")); - } - } - else if(nFeat) // This is a feat. - { - sSpellIcon = ""; - if(nSpell) - { - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - } - if(sSpellIcon == "" || sSpellIcon == "IR_USE") - { - sName = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))); - sSpellIcon = Get2DAString("feat", "ICON", nFeat); - } - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); - } - else // This is a spell. - { - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); - nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); - nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); - nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); - sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); - sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nMetaMagic, nDomain); - NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); - } - } - else - { - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); - NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString("")); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); - } - ++nIndex; - } - if(nIndex < 10) return; - // Row 6 Quick widget List2 - while(nIndex < 20) - { - jSpell = JsonArrayGet(jWidget, nIndex); - sIndex = IntToString(nIndex); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); - if(JsonGetType(jSpell) != JSON_TYPE_NULL) - { - nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); - nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); - nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); - if(nClass == -1) // This is an Item. - { - string sBaseName; - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - int nBaseItemType = JsonGetInt(JsonArrayGet(jSpell, 3)); - int nIprpSubType = JsonGetInt(JsonArrayGet(jSpell, 4)); - if(nSpell == SPELL_HEALINGKIT) - { - sName = "Healer's Kit +" + IntToString(nIprpSubType); - sSpellIcon = "isk_heal"; - sBaseName = "Healer's Kit"; - } - else if(nBaseItemType == BASE_ITEM_ENCHANTED_SCROLL || - nBaseItemType == BASE_ITEM_SCROLL || - nBaseItemType == BASE_ITEM_SPELLSCROLL) - { - sSpellIcon = Get2DAString("iprp_spells", "Icon", nIprpSubType); - sBaseName = "Scroll"; - } - else - { - if(nBaseItemType == BASE_ITEM_ENCHANTED_POTION || - nBaseItemType == BASE_ITEM_POTIONS) sBaseName = "Potion"; - else if(nBaseItemType == BASE_ITEM_ENCHANTED_WAND || - nBaseItemType == BASE_ITEM_MAGICWAND || - nBaseItemType == FEAT_CRAFT_WAND) sBaseName = "Wand"; - else sBaseName = ai_StripColorCodes(GetName(GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))))); - sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - } - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); - int nUses = ai_GetItemUses(oItem, nIprpSubType); - if(nUses) - { - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(TRUE)); - if(nUses == 999) sText = "Unlimited"; - else sText = IntToString(nUses); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sBaseName + " / " + sText + ")")); - } - } - else if(nFeat) // This is a feat. - { - sSpellIcon = ""; - if(nSpell) - { - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - } - if(sSpellIcon == "" || sSpellIcon == "IR_USE") - { - sName = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))); - sSpellIcon = Get2DAString("feat", "ICON", nFeat); - } - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); - } - else // This is a spell. - { - sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); - nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); - nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); - sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); - sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nMetaMagic, nDomain); - NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); - } - } - else - { - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); - NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString("")); - NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); - } - ++nIndex; - } + ai_PopulateWidgetList(oPC, oAssociate, nToken, JsonArrayGet(jSpells, 2)); } void ai_CreateSpellMemorizationNUI(object oPC, object oAssociate) { @@ -4228,7 +4460,13 @@ void ai_CreateSpellMemorizationNUI(object oPC, object oAssociate) jAIData = JsonArraySet(jAIData, 10, jSpells); ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); } - if(nClassSelected < 1 || nClassSelected > AI_MAX_CLASSES_PER_CHARACTER) nClassSelected = 1; + if(nClassSelected < 1 || nClassSelected > AI_MAX_CLASSES_PER_CHARACTER) + { + nClassSelected = 1; + jSpells = JsonArraySet(jSpells, 0, JsonInt(1)); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } nClass = GetClassByPosition(nClassSelected, oAssociate); int nMaxMemorizationSlots = GetMemorizedSpellCountByLevel(oAssociate, nClass, nLevelSelected); jRow = JsonArray(); @@ -4260,7 +4498,7 @@ void ai_CreateSpellMemorizationNUI(object oPC, object oAssociate) fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); } - string sText, sName = GetName(oAssociate); + string sText, sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; // Set the Layout of the window. @@ -4289,7 +4527,7 @@ void ai_CreateSpellMemorizationNUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_tooltip", JsonString(" " + sClass)); if(nClassSelected == nIndex) { - int nClassLevel = GetLevelByClass(nClass, oAssociate); + int nClassLevel = ai_GetCasterTotalLevel(oAssociate, nClass); string sSpellsGained = Get2DAString("classes", "SpellGainTable", nClass); int nMaxSpellLevel = StringToInt(Get2DAString(sSpellsGained, "NumSpellLevels", nClassLevel - 1)); for(nIndexLevel = 0; nIndexLevel <= 9; nIndexLevel++) @@ -4525,7 +4763,13 @@ void ai_CreateSpellKnownNUI(object oPC, object oAssociate) jAIData = JsonArraySet(jAIData, 10, jSpells); ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); } - if(nClassSelected < 1 || nClassSelected > AI_MAX_CLASSES_PER_CHARACTER) nClassSelected = 1; + if(nClassSelected < 1 || nClassSelected > AI_MAX_CLASSES_PER_CHARACTER) + { + nClassSelected = 1; + jSpells = JsonArraySet(jSpells, 0, JsonInt(1)); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } nClass = GetClassByPosition(nClassSelected, oAssociate); jRow = JsonArray(); for(nIndex = 0; nIndex < 10; nIndex++) @@ -4576,7 +4820,7 @@ void ai_CreateSpellKnownNUI(object oPC, object oAssociate) fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); } - string sText, sName = GetName(oAssociate); + string sText, sName = ai_StripColorCodes(GetName(oAssociate)); if(GetStringRight(sName, 1) == "s") sName = sName + "'"; else sName = sName + "'s"; // Set the Layout of the window. @@ -4605,7 +4849,7 @@ void ai_CreateSpellKnownNUI(object oPC, object oAssociate) NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_tooltip", JsonString(" " + sClass)); if(nClassSelected == nIndex) { - nClassLevel = GetLevelByClass(nClass, oAssociate); + nClassLevel = ai_GetCasterTotalLevel(oAssociate, nClass); sSpellsGained = Get2DAString("classes", "SpellGainTable", nClass); nMaxSpellLevel = StringToInt(Get2DAString(sSpellsGained, "NumSpellLevels", nClassLevel - 1)); for(nIndexLevel = 0; nIndexLevel <= 9; nIndexLevel++) diff --git a/src/module/nss/0i_menus_dm.nss b/src/module/nss/0i_menus_dm.nss index a2a2cfc..96fc93f 100644 --- a/src/module/nss/0i_menus_dm.nss +++ b/src/module/nss/0i_menus_dm.nss @@ -268,7 +268,7 @@ void ai_CreateDMOptionsNUI(object oPC) int nMonsterAI = (ResManGetAliasFor("ai_default", RESTYPE_NCS) != ""); int nAssociateAI = (ResManGetAliasFor("ai_a_default", RESTYPE_NCS) != ""); string sText = " [Single player]"; - if(AI_SERVER) sText = " [Server]"; + if(ai_GetIsServer()) sText = " [Server]"; // ************************************************************************* Width / Height // Row 1 ******************************************************************* 500 / 73 json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); @@ -282,6 +282,8 @@ void ai_CreateDMOptionsNUI(object oPC) jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 3 ******************************************************************* 500 / 129 jRow = CreateButton(JsonArray(), "Plugin Manager", "btn_plugin_manager", 160.0f, 20.0f, -1.0, "btn_plugin_manager_tooltip"); + //jRow = JsonArrayInsert(jRow, NuiSpacer()); + //jRow = CreateButtonSelect(jRow, "Effect Icons", "btn_effect_icon", 160.0f, 20.0f, "btn_effect_icon_tooltip"); jRow = JsonArrayInsert(jRow, NuiSpacer()); jRow = CreateButton(jRow, "Widget Manager", "btn_widget_manager", 160.0f, 20.0f, -1.0, "btn_widget_manager_tooltip"); // Add row to the column. @@ -331,7 +333,8 @@ void ai_CreateDMOptionsNUI(object oPC) jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_perception_distance", 2, FALSE, 35.0f, 20.0f, "txt_perception_distance_tooltip"); jGroupRow = CreateLabel(jGroupRow, "meters is the distance a monster can respond to allies.", "lbl_perception_distance", 411.0f, 20.0f, NUI_HALIGN_LEFT, 0, 0.0, "txt_perception_distance_tooltip"); jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - jGroupRow = CreateCheckBox(JsonArray(), " Monsters can prebuff before combat starts.", "chbx_buff_monsters", 450.0, 20.0); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters buff before combat starts.", "chbx_buff_monsters", 275.0, 20.0, "chbx_buff_monsters_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " Use all buff spells instead!", "chbx_full_buff", 210.0, 20.0, "chbx_full_buff_tooltip"); jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); jGroupRow = CreateCheckBox(JsonArray(), " Monsters can use summons before combat starts.", "chbx_buff_summons", 450.0, 20.0); jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); @@ -423,6 +426,10 @@ void ai_CreateDMOptionsNUI(object oPC) // Row 3 NuiSetBind(oPC, nToken, "btn_plugin_manager_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_plugin_manager_tooltip", JsonString(" Manages external executable scripts.")); + int bEffectIcon = ai_GetMagicMode(oPC, AI_MAGIC_EFFECT_ICON_REPORT); + //NuiSetBind(oPC, nToken, "btn_effect_icon", JsonBool (bEffectIcon)); + //NuiSetBind(oPC, nToken, "btn_effect_icon_event", JsonBool(TRUE)); + //NuiSetBind(oPC, nToken, "btn_effect_icon_tooltip", JsonString(" When on sends effect icon reports to the chat screen.")); NuiSetBind(oPC, nToken, "btn_widget_manager_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_widget_manager_tooltip", JsonString(" Manages widgets the players have access to.")); // Row 3 Label for AI RULES @@ -448,9 +455,15 @@ void ai_CreateDMOptionsNUI(object oPC) NuiSetBind(oPC, nToken, "txt_ai_difficulty_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "txt_ai_difficulty", JsonString(IntToString(GetLocalInt(oModule, AI_RULE_AI_DIFFICULTY)))); NuiSetBindWatch(oPC, nToken, "txt_ai_difficulty", TRUE); - NuiSetBind(oPC, nToken, "chbx_buff_monsters_check", JsonBool(GetLocalInt(oModule, AI_RULE_BUFF_MONSTERS))); + int bMonsterBuff = GetLocalInt(oModule, AI_RULE_BUFF_MONSTERS); + NuiSetBind(oPC, nToken, "chbx_buff_monsters_check", JsonBool(bMonsterBuff)); NuiSetBindWatch(oPC, nToken, "chbx_buff_monsters_check", TRUE); NuiSetBind(oPC, nToken, "chbx_buff_monsters_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_buff_monsters_tooltip", JsonString(" Monsters will cast all longer duration buff spells just before combat starts.")); + NuiSetBind(oPC, nToken, "chbx_full_buff_check", JsonBool(GetLocalInt(oModule, AI_RULE_FULL_BUFF_MONSTERS))); + NuiSetBindWatch(oPC, nToken, "chbx_full_buff_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_full_buff_event", JsonBool(bMonsterBuff)); + NuiSetBind(oPC, nToken, "chbx_full_buff_tooltip", JsonString(" Monsters will cast all buff spells just before combat starts! VERY DIFFICULTY!")); NuiSetBind(oPC, nToken, "chbx_buff_summons_check", JsonBool(GetLocalInt(oModule, AI_RULE_PRESUMMON))); NuiSetBindWatch(oPC, nToken, "chbx_buff_summons_check", TRUE); NuiSetBind(oPC, nToken, "chbx_buff_summons_event", JsonBool(TRUE)); @@ -876,6 +889,9 @@ void ai_CreateDMWidgetManagerNUI(object oPC) jRow = CreateLabel(JsonArray(), "Having a check next to a button will remove that button from the players menus.", "lbl_info2", 636.0, 15.0); jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 4 ******************************************************************* 575 / 162 + jRow = CreateButtonImage(jRow, "ir_invite", "btn_toggle_assoc_widget", 35.0f, 35.0f, 0.0, "btn_toggle_assoc_widget_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_toggle_assoc_widget", 25.0, 20.0, "btn_toggle_assoc_widget_tooltip"); + jRow = CreateButtonImage(JsonArray(), "ir_action", "btn_cmd_action", 35.0f, 35.0f, 0.0, "btn_cmd_action_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_cmd_action", 25.0, 20.0, "btn_cmd_action_tooltip"); @@ -903,10 +919,11 @@ void ai_CreateDMWidgetManagerNUI(object oPC) jRow = CreateButtonImage(jRow, "ir_scommand", "btn_cmd_ai_script", 35.0f, 35.0f, 0.0, "btn_cmd_ai_script_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_cmd_ai_script", 25.0, 20.0, "btn_cmd_ai_script_tooltip"); - jRow = CreateButtonImage(jRow, "isk_settrap", "btn_cmd_place_trap", 35.0f, 35.0f, 0.0, "btn_cmd_place_trap_tooltip"); - jRow = CreateCheckBox(jRow, "", "chbx_cmd_place_trap", 25.0, 20.0, "btn_cmd_place_trap_tooltip"); jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 5 ******************************************************************* 575 / 205 + jRow = CreateButtonImage(jRow, "isk_settrap", "btn_cmd_place_trap", 35.0f, 35.0f, 0.0, "btn_cmd_place_trap_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_place_trap", 25.0, 20.0, "btn_cmd_place_trap_tooltip"); + jRow = CreateButtonImage(JsonArray(), "isk_spellcraft", "btn_quick_widget", 35.0f, 35.0f, 0.0, "btn_quick_widget_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_quick_widget", 25.0, 20.0, "btn_quick_widget_tooltip"); @@ -934,10 +951,10 @@ void ai_CreateDMWidgetManagerNUI(object oPC) jRow = CreateButtonImage(jRow, "ir_examine", "btn_camera", 35.0f, 35.0f, 0.0, "btn_camera_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_camera", 25.0, 20.0, "btn_camera_tooltip"); - jRow = CreateButtonImage(jRow, "ir_pickup", "btn_inventory", 35.0f, 35.0f, 0.0, "btn_inventory_tooltip"); - jRow = CreateCheckBox(jRow, "", "chbx_inventory", 25.0, 20.0, "btn_inventory_tooltip"); jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 6 ******************************************************************* 575 / 248 + jRow = CreateButtonImage(jRow, "ir_pickup", "btn_inventory", 35.0f, 35.0f, 0.0, "btn_inventory_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_inventory", 25.0, 20.0, "btn_inventory_tooltip"); jRow = CreateButtonImage(JsonArray(), "ife_familiar", "btn_familiar", 35.0f, 35.0f, 0.0, "btn_familiar_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_familiar", 25.0, 20.0, "btn_familiar_tooltip"); @@ -966,10 +983,10 @@ void ai_CreateDMWidgetManagerNUI(object oPC) jRow = CreateButtonImage(jRow, "ir_open", "btn_open_door", 35.0f, 35.0f, 0.0, "btn_open_door_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_open_door", 25.0, 20.0, "btn_open_door_tooltip"); - jRow = CreateButtonImage(jRow, "isk_distrap", "btn_traps", 35.0f, 35.0f, 0.0, "btn_traps_tooltip"); - jRow = CreateCheckBox(jRow, "", "chbx_traps", 25.0, 20.0, "btn_traps_tooltip"); jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 7 ******************************************************************* 575 / 291 + jRow = CreateButtonImage(jRow, "isk_distrap", "btn_traps", 35.0f, 35.0f, 0.0, "btn_traps_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_traps", 25.0, 20.0, "btn_traps_tooltip"); jRow = CreateButtonImage(JsonArray(), "isk_olock", "btn_pick_locks", 35.0f, 35.0f, 0.0, "btn_pick_locks_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_pick_locks", 25.0, 20.0, "btn_pick_locks_tooltip"); @@ -998,10 +1015,11 @@ void ai_CreateDMWidgetManagerNUI(object oPC) jRow = CreateButtonImage(jRow, "isk_heal", "btn_heal_out", 35.0f, 35.0f, 0.0, "btn_heal_out_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_heal_out", 25.0, 20.0, "btn_heal_out_tooltip"); - jRow = CreateButtonImage(jRow, "dm_heal", "btn_heal_in", 35.0f, 35.0f, 0.0, "btn_heal_in_tooltip"); - jRow = CreateCheckBox(jRow, "", "chbx_heal_in", 25.0, 20.0, "btn_heal_in_tooltip"); jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 8 ******************************************************************* 575 / 334 + jRow = CreateButtonImage(jRow, "dm_heal", "btn_heal_in", 35.0f, 35.0f, 0.0, "btn_heal_in_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_heal_in", 25.0, 20.0, "btn_heal_in_tooltip"); + jRow = CreateButtonImage(JsonArray(), "ir_heal", "btn_heals_onoff", 35.0f, 35.0f, 0.0, "btn_heals_onoff_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_heals_onoff", 25.0, 20.0, "btn_heals_onoff_tooltip"); @@ -1023,7 +1041,6 @@ void ai_CreateDMWidgetManagerNUI(object oPC) jRow = CreateButtonImage(jRow, "ir_dmchat", "btn_perc_range", 35.0f, 35.0f, 0.0, "btn_perc_range_tooltip"); jRow = CreateCheckBox(jRow, "", "chbx_perc_range", 25.0, 20.0, "btn_perc_range_tooltip"); - // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); float fHeight = 334.0; // Get the window location to restore it from the database. @@ -1049,6 +1066,7 @@ void ai_CreateDMWidgetManagerNUI(object oPC) // Row 2 & 3 Labels. // Load all the buttons states. //int bAIWidgetLock = ai_GetDMWAccessButton(BTN_WIDGET_LOCK); + int bAssocWidgetOff = ai_GetDMWAccessButton(BTN_ASSOC_WIDGETS_OFF); int bCmdAction = ai_GetDMWAccessButton(BTN_CMD_ACTION); int bCmdGuard = ai_GetDMWAccessButton(BTN_CMD_GUARD); int bCmdHold = ai_GetDMWAccessButton(BTN_CMD_HOLD); @@ -1101,6 +1119,12 @@ void ai_CreateDMWidgetManagerNUI(object oPC) SetLocalInt(oPC, "CHBX_SKIP", TRUE); DelayCommand(2.0, DeleteLocalInt(oPC, "CHBX_SKIP")); // Row 4 + NuiSetBind(oPC, nToken, "chbx_toggle_assoc_widget_check", JsonBool (bCmdAction)); + NuiSetBindWatch(oPC, nToken, "chbx_toggle_assoc_widget_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_toggle_assoc_widget_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_toggle_assoc_widget_tooltip", JsonString(" Associate widgets")); + NuiSetBind(oPC, nToken, "chbx_cmd_action_check", JsonBool (bCmdAction)); NuiSetBindWatch(oPC, nToken, "chbx_cmd_action_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_action_event", JsonBool(TRUE)); @@ -1154,13 +1178,13 @@ void ai_CreateDMWidgetManagerNUI(object oPC) NuiSetBind(oPC, nToken, "chbx_cmd_ai_script_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_cmd_ai_script_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_cmd_ai_script_tooltip", JsonString(" Combat Tactics button")); - + // Row 5 NuiSetBind(oPC, nToken, "chbx_cmd_place_trap_check", JsonBool (bCmdPlacetrap)); NuiSetBindWatch (oPC, nToken, "chbx_cmd_place_trap_check", TRUE); NuiSetBind(oPC, nToken, "chbx_cmd_place_trap_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_cmd_place_trap_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_cmd_place_trap_tooltip", JsonString (" Place Trap button")); - // Row 5 + NuiSetBind(oPC, nToken, "chbx_quick_widget_check", JsonBool (bSpellWidget)); NuiSetBindWatch (oPC, nToken, "chbx_quick_widget_check", TRUE); NuiSetBind(oPC, nToken, "chbx_quick_widget_event", JsonBool(TRUE)); @@ -1214,13 +1238,13 @@ void ai_CreateDMWidgetManagerNUI(object oPC) NuiSetBind(oPC, nToken, "chbx_camera_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_camera_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_camera_tooltip", JsonString(" Change Camera button")); - + // Row 6 NuiSetBind(oPC, nToken, "chbx_inventory_check", JsonBool (bInventory)); NuiSetBindWatch (oPC, nToken, "chbx_inventory_check", TRUE); NuiSetBind(oPC, nToken, "chbx_inventory_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_inventory_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_inventory_tooltip", JsonString(" Open Inventory button")); - // Row 6 + NuiSetBind(oPC, nToken, "chbx_familiar_check", JsonBool(bBtnFamiliar)); NuiSetBindWatch (oPC, nToken, "chbx_familiar_check", TRUE); NuiSetBind(oPC, nToken, "chbx_familiar_event", JsonBool(TRUE)); @@ -1274,13 +1298,13 @@ void ai_CreateDMWidgetManagerNUI(object oPC) NuiSetBind(oPC, nToken, "chbx_open_door_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_open_door_event", JsonBool(TRUE)); NuiSetBind (oPC, nToken, "btn_open_door_tooltip", JsonString(" Open Door button")); - + // Row 7 NuiSetBind(oPC, nToken, "chbx_traps_check", JsonBool(bTraps)); NuiSetBindWatch (oPC, nToken, "chbx_traps_check", TRUE); NuiSetBind(oPC, nToken, "chbx_traps_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_traps_event", JsonBool(TRUE)); NuiSetBind (oPC, nToken, "btn_traps_tooltip", JsonString(" Disable Traps button")); - // Row 7 + NuiSetBind(oPC, nToken, "chbx_pick_locks_check", JsonBool(bPickLocks)); NuiSetBindWatch(oPC, nToken, "chbx_pick_locks_check", TRUE); NuiSetBind(oPC, nToken, "chbx_pick_locks_event", JsonBool(TRUE)); @@ -1334,13 +1358,13 @@ void ai_CreateDMWidgetManagerNUI(object oPC) NuiSetBind(oPC, nToken, "chbx_heal_out_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_heal_out_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_heal_out_tooltip", JsonString(" Heal Out of Combat button")); - + // Row 8 NuiSetBind(oPC, nToken, "chbx_heal_in_check", JsonBool(bHealIn)); NuiSetBindWatch (oPC, nToken, "chbx_heal_in_check", TRUE); NuiSetBind(oPC, nToken, "chbx_heal_in_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_heal_in_event", JsonBool (TRUE)); NuiSetBind(oPC, nToken, "btn_heal_in_tooltip", JsonString(" Heal In Combat button")); - // Row 8 + NuiSetBind(oPC, nToken, "chbx_heals_onoff_check", JsonBool(bSelfHealOnOff)); NuiSetBindWatch (oPC, nToken, "chbx_heals_onoff_check", TRUE); NuiSetBind(oPC, nToken, "chbx_heals_onoff_event", JsonBool(TRUE)); diff --git a/src/module/nss/0i_module.nss b/src/module/nss/0i_module.nss index 6d50182..0fb394f 100644 --- a/src/module/nss/0i_module.nss +++ b/src/module/nss/0i_module.nss @@ -7,15 +7,18 @@ #include "x2_inc_switches" #include "0i_associates" #include "0i_menus" +#include "0i_menus_dm" #include "0i_player_target" #include "0i_gui_events" // Add to nw_c2_default9 OnSpawn event script of monsters and int ai_OnMonsterSpawn(object oCreature); // Add to nw_ch_ac9 OnSpawn event script of henchman. void ai_OnAssociateSpawn(object oCreature); -// Run all of the players starting scripts. +// Run all of the game setup scripts and build for PC. // If oPC is passed as Invalid then it will get the firt PC in the game. void ai_CheckPCStart(object oPC = OBJECT_INVALID); +// Run all of the games setup scripts and build for DM. +void ai_CheckDMStart(object oDM); // Checks to see if we should change the monster via Json. int ai_ChangeMonster(object oCreature, object oModule); // Checks to see if we should change the associate via Json. @@ -45,7 +48,7 @@ int ai_OnMonsterSpawn(object oCreature) int nInfiniteDungeons; int nPRC = GetLocalInt(oModule, AI_USING_PRC); // If you are running a server this will not affect the module. - if(!AI_SERVER) + if(!ai_GetIsServer()) { ai_CheckPCStart(); string sModuleName = GetModuleName(); @@ -107,7 +110,7 @@ void ai_OnAssociateSpawn(object oCreature) SetLocalInt(oCreature, AI_ONSPAWN_EVENT, TRUE); int bPRC = GetLocalInt(GetModule(), AI_USING_PRC); // If you are running a server this will not affect the module. - if(!AI_SERVER) + if(!ai_GetIsServer()) { if(bPRC) ai_SetPRCAssociateEventScripts(oCreature); } @@ -117,8 +120,17 @@ void ai_OnAssociateSpawn(object oCreature) // We change this script so we can setup permanent summons on/off. // If you don't use this you may remove the next three lines. string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH); - SetLocalString(oCreature, "AI_ON_DEATH", sScript); - SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH, "0e_ch_7_ondeath"); + // If our script is set in the OnDeath event then don't save as secondary. + if(sScript != "0e_ch_7_ondeath") + { + SetLocalString(oCreature, "AI_ON_DEATH", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH, "0e_ch_7_ondeath"); + } + else if(GetLocalString(oCreature, "AI_ON_DEATH") == "0e_ch_7_ondeath") + { + // If we have somehow saved our death script then change to default. + SetLocalString(oCreature, "AI_ON_DEATH", "nw_ch_ac7"); + } } // Initialize Associate modes for basic use. ai_SetListeningPatterns(oCreature); @@ -147,12 +159,28 @@ void ai_CheckPCStart(object oPC = OBJECT_INVALID) ai_SetAIRules(); ai_CheckAssociateData(oPC, oPC, "pc"); ai_StartupPlugins(oPC); - ai_SetupPlayerTarget(oPC); - ai_SetupModuleGUIEvents(oPC); + ai_SetupPlayerTarget(); + ai_SetupModuleGUIEvents(); ai_CreateWidgetNUI(oPC, oPC); ai_SetNormalAppearance(oPC); } } +void ai_CheckDMStart(object oDM) +{ + if(!NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI)) + { + object oModule = GetModule(); + // Do PRC check and save variable to the module. + if(ResManGetAliasFor("prc_ai_fam_percp", RESTYPE_NCS) != "") + SetLocalInt(oModule, AI_USING_PRC, TRUE); + ai_SetAIRules(); + ai_CheckDMData(oDM); + ai_StartupPlugins(oDM); + ai_SetupPlayerTarget(); + ai_SetupModuleGUIEvents(); + ai_CreateDMWidgetNUI(oDM); + } +} void ai_CopyMonster(object oCreature, object oModule) { // After setting the monster lets see if we should copy it. @@ -179,6 +207,7 @@ void ai_CreateMonster(json jCreature, location lLocation, object oModule) { //WriteTimestampedLogEntry("0i_module, 181, " + JsonDump(jCreature, 1)); object oCreature = JsonToObject(jCreature, lLocation, OBJECT_INVALID, TRUE); + if(AI_DEBUG) ai_Debug("0i_module", "210", "Creating: " + GetName(oCreature)); // Lets set the new version as spawned so we skip the initial setup again. SetLocalInt(oCreature, AI_ONSPAWN_EVENT, TRUE); /*if(GetLocalInt(oModule, AI_RULE_CORPSES_STAY)) @@ -188,6 +217,12 @@ void ai_CreateMonster(json jCreature, location lLocation, object oModule) } */ if(AI_DEBUG) ai_Debug("0i_module", "187", GetName(oCreature)); ai_CopyMonster(oCreature, oModule); + // This is a hak to allow wild shaped creatures to be able to attack! + if(GetHasFeat(FEAT_WILD_SHAPE, oCreature)) + { + AssignCommand(oCreature, ActionUseFeat(FEAT_WILD_SHAPE, oCreature, SUBFEAT_WILD_SHAPE_BADGER)); + DelayCommand(4.0, ai_RemoveASpecificEffect(oCreature, EFFECT_TYPE_POLYMORPH)); + } return; } json ai_SetCompanionSummoning(object oCreature, json jCreature) @@ -198,7 +233,7 @@ json ai_SetCompanionSummoning(object oCreature, json jCreature) jFamiliar = JsonObjectSet(jFamiliar, "value", JsonString("Summoned Familiar")); jCreature = JsonObjectSet(jCreature, "FamiliarName", jFamiliar); jFamiliar = JsonObjectGet(jCreature, "FamiliarType"); - jFamiliar = JsonObjectSet(jFamiliar, "value", JsonInt(Random(11))); + jFamiliar = JsonObjectSet(jFamiliar, "value", JsonInt(10)); //JsonInt(Random(11))); return JsonObjectSet(jCreature, "FamiliarType", jFamiliar); } if(GetHasFeat(FEAT_ANIMAL_COMPANION , oCreature, TRUE)) @@ -217,9 +252,9 @@ int ai_ChangeMonster(object oCreature, object oModule) object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oCreature); // Lets not mess up the cutscenes with silly RULES. if(GetCutsceneMode(oPC)) return FALSE; - //float fDistance = GetDistanceBetween(oCreature, oPC); + float fDistance = GetDistanceBetween(oCreature, oPC); // Looks bad to see creatures wink in and out plus could cause module errors. - //if(fDistance != 0.0 && fDistance < AI_RANGE_PERCEPTION) return oCreature; + if(fDistance != 0.0 && fDistance < 20.0) return FALSE; if(IsInConversation(oCreature)) return FALSE; json jCreature = ObjectToJson(oCreature, TRUE); // We now use plugins to mod our monsters. @@ -238,8 +273,8 @@ int ai_ChangeMonster(object oCreature, object oModule) jCreature = GetLocalJson(oModule, AI_MONSTER_JSON); } int nSummon = GetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS) && - (GetHasFeat(FEAT_SUMMON_FAMILIAR, oCreature, TRUE)) || - GetHasFeat(FEAT_ANIMAL_COMPANION, oCreature, TRUE); + (GetHasFeat(FEAT_SUMMON_FAMILIAR, oCreature, TRUE) || + GetHasFeat(FEAT_ANIMAL_COMPANION, oCreature, TRUE)); int nPercDist = GetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE) != 11 && GetReputation(oCreature, oPC) < 11; //WriteTimestampedLogEntry(GetName(oCreature) + ": fDistance: " + FloatToString(fDistance, 0, 2) + " nSummon: " + IntToString(nSummon) + @@ -256,10 +291,12 @@ int ai_ChangeMonster(object oCreature, object oModule) if(nSummon) jCreature = ai_SetCompanionSummoning(oCreature, jCreature); SetLocalInt(oModule, AI_MONSTER_CHANGED, TRUE); } + // Did any of the Monster mods get used? These are done in independent mod scripts. if(GetLocalInt(oModule, AI_MONSTER_CHANGED)) { SetIsDestroyable(TRUE, FALSE, FALSE, oCreature); location lLocation = GetLocation(oCreature); + if(AI_DEBUG) ai_Debug("0i_module", "299", "Destroying: " + GetName(oCreature)); DestroyObject(oCreature); AssignCommand(oModule, DelayCommand(1.0, ai_CreateMonster(jCreature, lLocation, oModule))); DeleteLocalInt(oModule, AI_MONSTER_CHANGED); @@ -268,11 +305,18 @@ int ai_ChangeMonster(object oCreature, object oModule) else ai_CopyMonster(oCreature, oModule); DeleteLocalJson(oModule, AI_MONSTER_JSON); DeleteLocalObject(oModule, AI_MONSTER_OBJECT); + // This is a hak to allow wild shaped creatures to be able to attack! + if(GetHasFeat(FEAT_WILD_SHAPE)) + { + AssignCommand(oCreature, ActionUseFeat(FEAT_WILD_SHAPE, oCreature, SUBFEAT_WILD_SHAPE_BADGER)); + DelayCommand(4.0, ai_RemoveASpecificEffect(oCreature, EFFECT_TYPE_POLYMORPH)); + } return FALSE; } // Special event scripts for Infinite Dungeons! void ai_SetIDMonsterEventScripts(object oCreature) { + if(GetIsPC(oCreature)) return; //if(AI_DEBUG) ai_Debug("0i_module", "433", "Changing " + GetName(oCreature) + "'s Infinte Dungeons event scripts."); //********** On Heartbeat ********** string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); @@ -355,6 +399,7 @@ void ai_SetIDMonsterEventScripts(object oCreature) // Special event scripts for Infinite Dungeons with PRC! void ai_SetPRCIDMonsterEventScripts(object oCreature) { + if(GetIsPC(oCreature)) return; //if(AI_DEBUG) ai_Debug("0i_module", "433", "Changing " + GetName(oCreature) + "'s Infinte Dungeons event scripts for PRC."); //********** On Heartbeat ********** string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); @@ -437,6 +482,7 @@ void ai_SetPRCIDMonsterEventScripts(object oCreature) // Special event scripts for PRC associates! void ai_SetPRCAssociateEventScripts(object oCreature) { + if(GetIsPC(oCreature)) return; //if(AI_DEBUG) ai_Debug("0i_module", "433", "Changing " + GetName(oCreature) + "'s Infinte Dungeons event scripts for PRC."); //********** On Heartbeat ********** string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); @@ -494,6 +540,7 @@ void ai_SetPRCAssociateEventScripts(object oCreature) } void ai_ChangeEventScriptsForMonster(object oCreature) { + if(GetIsPC(oCreature)) return; //********** On Heartbeat ********** string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); if(sScript == "0e_c2_1_hb") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "nw_c2_default1"); @@ -528,6 +575,7 @@ void ai_ChangeEventScriptsForMonster(object oCreature) } void ai_ChangeEventScriptsForAssociate(object oCreature) { + if(GetIsPC(oCreature)) return; SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "nw_ch_ac1"); SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "nw_ch_ac2"); SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "nw_ch_ac3"); diff --git a/src/module/nss/0i_player_target.nss b/src/module/nss/0i_player_target.nss index bed4834..5cdba8e 100644 --- a/src/module/nss/0i_player_target.nss +++ b/src/module/nss/0i_player_target.nss @@ -8,11 +8,9 @@ #include "0i_menus" // Setup an AI OnPlayerTarget Event script while allowing any module onplayer // target event script to still work. -void ai_SetupPlayerTarget(object oCreature); +void ai_SetupPlayerTarget(); // Selects a target for oAssocite to follow. void ai_AllSelectTarget(object oPC, object oAssociate, object oTarget); -// Removes the Cutscene ghosts and variables from all associates. For original AI scripts. -void ai_OriginalRemoveAllActionMode(object oPC); // Removes the Cutscene ghosts and Command mode from all associates. void ai_RemoveAllActionMode(object oPC); // Once a trap has been selected from the associates inventory move to placing the trap. @@ -30,7 +28,14 @@ void ai_UpdateAssociateWidget(object oPC, object oAssociate); // Sets oAssociates action mode for nFeat from the quick widget menu int ai_SetActionMode(object oAssociate, int nFeat); -void ai_SetupPlayerTarget(object oCreature) +void ai_EnterAssociateTargetMode(object oPC, object oAssociate) +{ + SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate); + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION"); + SetLocalInt(oPC, AI_TARGET_MODE_ON, TRUE); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_SetupPlayerTarget() { object oModule = GetModule(); string sModuleTargetEvent = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET); @@ -40,129 +45,7 @@ void ai_SetupPlayerTarget(object oCreature) } SetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET, "0e_player_target"); } -void ai_OriginalActionAssociate(object oPC, object oTarget, location lLocation) -{ - object oAssociate = OBJECT_SELF; - if(!GetLocalInt(oAssociate, sGhostModeVarname) && GetLocalInt(oPC, sGhostModeVarname)) - { - effect eGhost = EffectCutsceneGhost(); - ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oAssociate); - SetLocalInt(oAssociate, sGhostModeVarname, TRUE); - } - int nObjectType = GetObjectType(oTarget); - ai_ClearCreatureActions(TRUE); - if(oTarget == GetArea(oPC)) - { - ActionMoveToLocation(lLocation, TRUE); - if(GetLocalObject(oPC, AI_FOLLOW_TARGET) == oAssociate) - { - float fFollowDistance = 3.0; - AssignCommand(oPC, ai_ClearCreatureActions()); - AssignCommand(oPC, ActionForceFollowObject(oAssociate, fFollowDistance)); - } - } - else if(nObjectType == OBJECT_TYPE_CREATURE) - { - if(oTarget != GetLocalObject(oPC, AI_TARGET_ASSOCIATE)) - { - if(GetMaster(oTarget) == oPC) - { - SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION"); - SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oTarget); - ai_SendMessages(GetName(oTarget) + " is now in Action Mode.", AI_COLOR_YELLOW, oPC); - } - else ActionMoveToObject(oTarget, TRUE); - } - } - else if(nObjectType == OBJECT_TYPE_DOOR) - { - if(GetIsTrapped(oTarget) && GetAssociateState(NW_ASC_DISARM_TRAPS, oAssociate)) - { - if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate); - if(GetTrapDetectedBy(oTarget, oAssociate)) - { - bkAttemptToDisarmTrap(oTarget); - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); - return; - } - } - if(GetLocked(oTarget)) bkAttemptToOpenLock(oTarget); - if(GetIsOpen(oTarget)) - { - ActionCloseDoor(oTarget, TRUE); - } - else ActionOpenDoor(oTarget, TRUE); - } - else if(nObjectType == OBJECT_TYPE_ITEM) - { - ActionPickUpItem(oTarget); - } - else if(nObjectType == OBJECT_TYPE_PLACEABLE) - { - ActionMoveToObject(oTarget, TRUE); - if(GetHasInventory(oTarget)) - { - if(GetIsTrapped(oTarget) && GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate)) - { - if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate); - if(GetTrapDetectedBy(oTarget, oAssociate)) - { - bkAttemptToDisarmTrap(oTarget); - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); - return; - } - if(GetLocked(oTarget)) - { - if(GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate)) - { - bkAttemptToOpenLock(oTarget); - } - else AssignCommand(oAssociate, ai_HaveCreatureSpeak(oAssociate, 0, "This " + GetName(oTarget) + " is locked!")); - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); - return; - } - DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE); - } - else if(GetLocked(oTarget)) - { - if(GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate)) - { - bkAttemptToOpenLock(oTarget); - } - else AssignCommand(oAssociate, ai_HaveCreatureSpeak(oAssociate, 0, "This " + GetName(oTarget) + " is locked!")); - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); - return; - } - DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE); - } - DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE); - } - else if(nObjectType == OBJECT_TYPE_TRIGGER) - { - if(GetIsTrapped(oTarget) && GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate)) - { - if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate); - if(GetTrapDetectedBy(oTarget, oAssociate)) bkAttemptToDisarmTrap(oTarget); - } - } - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); -} -void ai_OriginalActionAllAssociates(object oPC, object oTarget, location lLocation) -{ - object oAssociate; - int nIndex; - for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) - { - oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); - if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_OriginalActionAssociate(oPC, oTarget, lLocation)); - } - for(nIndex = 2; nIndex < 6; nIndex++) - { - oAssociate = GetAssociate(nIndex, oPC); - if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_OriginalActionAssociate(oPC, oTarget, lLocation)); - } -} -void ai_ActionAssociate(object oPC, object oTarget, location lLocation) +void ai_ActionAssociate(object oPC, object oTarget, location lLocation, int bActionAll = FALSE) { object oAssociate = OBJECT_SELF; if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) && @@ -204,12 +87,14 @@ void ai_ActionAssociate(object oPC, object oTarget, location lLocation) { SetLocalString(oAssociate, AI_COMBAT_SCRIPT, GetLocalString(oAssociate, AI_DEFAULT_SCRIPT)); } - if(ai_GetIsInCombat(oAssociate)) ai_DoAssociateCombatRound(oAssociate, oTarget); - else - { - ai_HaveCreatureSpeak(oAssociate, 5, ":0:1:2:3:6:"); - ai_StartAssociateCombat(oAssociate, oTarget); - } + //if(ai_GetIsInCombat(oAssociate)) ai_DoAssociateCombatRound(oAssociate, oTarget); + //else + //{ + // ai_HaveCreatureSpeak(oAssociate, 5, ":0:1:2:3:6:"); + // ai_StartAssociateCombat(oAssociate, oTarget); + //} + if(ai_GetIsRangeWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oAssociate))) ActionAttack(oTarget, TRUE); + else ActionAttack(oTarget); ai_SendMessages(GetName(oAssociate) + " is attacking " + GetName(oTarget), AI_COLOR_RED, oPC); } else @@ -231,7 +116,7 @@ void ai_ActionAssociate(object oPC, object oTarget, location lLocation) if(ai_ReactToTrap(oAssociate, oTarget, TRUE)) bStopAction = TRUE; if(bStopAction) { - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + ai_EnterAssociateTargetMode(oPC, oAssociate); return; } } @@ -256,7 +141,7 @@ void ai_ActionAssociate(object oPC, object oTarget, location lLocation) { if(ai_ReactToTrap(oAssociate, oTarget, TRUE)) { - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + ai_EnterAssociateTargetMode(oPC, oAssociate); return; } @@ -289,7 +174,7 @@ void ai_ActionAssociate(object oPC, object oTarget, location lLocation) if(GetTrapDetectedBy(oTarget, oAssociate)) ai_ReactToTrap(oAssociate, oTarget, TRUE); } } - EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + if(!bActionAll) ai_EnterAssociateTargetMode(oPC, oAssociate); } void ai_ActionAllAssociates(object oPC, object oTarget, location lLocation) { @@ -298,12 +183,12 @@ void ai_ActionAllAssociates(object oPC, object oTarget, location lLocation) for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) { oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); - if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation)); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation, TRUE)); } for(nIndex = 2; nIndex < 6; nIndex++) { oAssociate = GetAssociate(nIndex, oPC); - if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation)); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation, TRUE)); } } void ai_SelectFollowTarget(object oPC, object oAssociate, object oTarget) @@ -317,6 +202,7 @@ void ai_SelectFollowTarget(object oPC, object oAssociate, object oTarget) { ai_SetAIMode(oAssociate, AI_MODE_FOLLOW, FALSE); DeleteLocalObject(oAssociate, AI_FOLLOW_TARGET); + ClearAllActions(FALSE, oAssociate); string sTarget; if(ai_GetIsCharacter(oAssociate)) { @@ -340,34 +226,6 @@ void ai_SelectFollowTarget(object oPC, object oAssociate, object oTarget) } aiSaveAssociateModesToDb(oPC, oAssociate); } -void ai_OriginalRemoveAllActionMode(object oPC) -{ - if(!ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST)) return; - object oAssociate; - int nIndex; - for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) - { - oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); - if(oAssociate != OBJECT_INVALID && - !ai_GetAIMode(oAssociate, AI_MODE_GHOST) && - GetLocalInt(oAssociate, sGhostModeVarname)) - { - ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); - DeleteLocalInt(oAssociate, sGhostModeVarname); - } - } - for(nIndex = 2; nIndex < 6; nIndex++) - { - oAssociate = GetAssociate(nIndex, oPC); - if(oAssociate != OBJECT_INVALID && - !ai_GetAIMode(oAssociate, AI_MODE_GHOST) && - GetLocalInt(oAssociate, sGhostModeVarname)) - { - ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); - DeleteLocalInt(oAssociate, sGhostModeVarname); - } - } -} void ai_RemoveAllActionMode(object oPC) { object oAssociate; @@ -634,21 +492,23 @@ void ai_SelectWidgetSpellTarget(object oPC, object oAssociate, string sElem) json jSpell = JsonArrayGet(jWidget, nIndex); int nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); int nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate); if(nClass == -1) // This is an Item. { object oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); int nBaseItemType = GetBaseItemType(oItem); + int nIprpSubType = JsonGetInt(JsonArrayGet(jSpell, 4)); + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + if(nIprpSubType == GetItemPropertySubType(ipProperty)) break; + ipProperty = GetNextItemProperty(oItem); + } if(Get2DAString("spells", "Range", nSpell) == "P" || // Self nBaseItemType == BASE_ITEM_ENCHANTED_POTION || - nBaseItemType == BASE_ITEM_POTIONS) + nBaseItemType == BASE_ITEM_POTIONS || + nIprpSubType == IP_CONST_CASTSPELL_UNIQUE_POWER_SELF_ONLY) { - int nIprpSubType = JsonGetInt(JsonArrayGet(jSpell, 4)); - itemproperty ipProperty = GetFirstItemProperty(oItem); - while(GetIsItemPropertyValid(ipProperty)) - { - if(nIprpSubType == GetItemPropertySubType(ipProperty)) break; - ipProperty = GetNextItemProperty(oItem); - } if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oAssociate)); DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); @@ -693,8 +553,7 @@ void ai_SelectWidgetSpellTarget(object oPC, object oAssociate, string sElem) EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_ATTACK, MOUSECURSOR_NOATTACK); } // Check feat and adjust if it is an action mode feat. - if(ai_SetActionMode(oAssociate, nFeat)) return; - AssignCommand(oAssociate, ActionUseFeat(nFeat, oAssociate, nSpell)); + if(!ai_SetActionMode(oAssociate, nFeat)) AssignCommand(oAssociate, ActionUseFeat(nFeat, oAssociate, nSpell)); DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); return; } @@ -702,7 +561,6 @@ void ai_SelectWidgetSpellTarget(object oPC, object oAssociate, string sElem) } else SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_CAST_SPELL"); } - SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate); int nObjectType; string sTarget = Get2DAString("spells", "TargetType", nSpell); int nTarget = ai_HexStringToInt(sTarget); @@ -784,6 +642,7 @@ int ai_SetActionMode(object oAssociate, int nFeat) else if(nFeat == FEAT_EXPERTISE) nMode = ACTION_MODE_EXPERTISE; else if(nFeat == FEAT_IMPROVED_EXPERTISE) nMode = ACTION_MODE_IMPROVED_EXPERTISE; else if(nFeat == FEAT_DIRTY_FIGHTING) nMode = ACTION_MODE_DIRTY_FIGHTING; + else if(nFeat == FEAT_DWARVEN_DEFENDER_DEFENSIVE_STANCE) nMode = 12; // ACTION_MODE_DEFENSIVE_STANCE if(nMode) { SetActionMode(oAssociate, nMode, !GetActionMode(oAssociate, nMode)); diff --git a/src/module/nss/0i_spells.nss b/src/module/nss/0i_spells.nss index b7b75e2..0440d30 100644 --- a/src/module/nss/0i_spells.nss +++ b/src/module/nss/0i_spells.nss @@ -69,6 +69,8 @@ struct stSpell int nMaxSlots; int nSlot; }; +// Gets the total caster levels for nClass for oCreature. +int ai_GetCasterTotalLevel(object oCreature, int nClass); // Returns TRUE if oCreature can cast nSpell from nLevel. int ai_GetCanCastSpell(object oCreature, int nSpell, int nClass, int nLevel, int nMetaMagic = 0, int nDomain = 0); // Returns TRUE if oCreature is immune to petrification. @@ -147,12 +149,12 @@ void ai_SetupAllyHealingTargets(object oCaster, object oPC); // Clears the casters buff targets. void ai_ClearBuffTargets(object oCaster, string sVariable); // Cycles through a casters spells casting all buffs via actions. -void ai_ActionCastMemorizedBuff(struct stSpell stSpell); +void ai_ActionCastMemorizedBuff(struct stSpell stSpell, float fDelay, int bInstantSpell); // Cycles through a casters spells casting all buffs via actions. -void ai_ActionCastKnownBuff(struct stSpell stSpell); +void ai_ActionCastKnownBuff(struct stSpell stSpell, float fDelay, int bInstantSpell); // Checks oCaster for buffing spells and casts them based on nTarget; -// These are cast as actions and will happen at the speed based on -// AI_HENCHMAN_BUFF_DELAY, but are still actions. +// These are cast as actions and will happen at the speed based on the delay set +// by the player. 6.0 seconds to 0.1 second. Default 0.1 second. // nTarget is 0-9 where 0 is all targets, 1 is oPC, 2 is the caster // 3 Familiar, 4 is Animal Companion, 5 is Summons, 6 is Dominated, and 7+ is henchman. // Targets must be defined in variable AI_ALLY_TARGET_* where * is 1 to #. @@ -161,6 +163,12 @@ void ai_CastBuffs(object oCaster, int nBuffType, int nTarget, object oPC); // Returns TRUE if oCaster cast spontaneous cure spell on oTarget. // This uses an action and must use AssignCommand or OBJECT_SELF is the caster! int ai_CastSpontaneousCure(object oCreature, object oTarget, object oPC); +// Returns TRUE if oCaster casts a memorized inflict spell on oTarget. +// This uses an action and must use AssignCommand or OBJECT_SELF is the caster! +int ai_CastMemorizedInflict(object oCreature, object oTarget, object oPC, int nClass); +// Returns TRUE if oCaster casts a known inflict spell on oTarget. +// This uses an action and must use AssignCommand or OBJECT_SELF is the caster! +int ai_CastKnownInflict(object oCreature, object oTarget, object oPC, int nClass); // Returns TRUE if oCaster casts a memorized cure spell on oTarget. // This uses an action and must use AssignCommand or OBJECT_SELF is the caster! int ai_CastMemorizedHealing(object oCreature, object oTarget, object oPC, int nClass); @@ -176,6 +184,8 @@ void ai_SpellConcentrationCheck(object oCaster); int ai_CastInMelee(object oCreature, int nSpell, int nInMelee); // Returns a float range for the caster to search for a target of an offensive spell. float ai_GetOffensiveSpellSearchRange(object oCreature, int nSpell); +// Returns TRUE if nSpell is an inflict spell and will not over heal for nDamage. +int ai_ShouldWeCastThisInflictSpell(int nSpell, int nDamage); // Returns TRUE if nSpell is a cure spell and will not over heal for nDamage. int ai_ShouldWeCastThisCureSpell(int nSpell, int nDamage); // Casts the spell on the current target for oAssociate. @@ -184,6 +194,23 @@ void ai_CastWidgetSpell(object oPC, object oAssociate, object oTarget, location void ai_UseWidgetFeat(object oPC, object oAssociate, object oTarget, location lLocation); // Uses the item on the current target for oAssociate. void ai_UseWidgetItem(object oPC, object oAssociate, object oTarget, location lLocation); +int ai_GetCasterTotalLevel(object oCreature, int nClass) +{ + int nIndex, nCheckClass; + int nLevel = GetLevelByClass(nClass, oCreature); + if(nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_WIZARD) + { + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex ++) + { + nCheckClass = GetClassByPosition(nIndex, oCreature); + if(nCheckClass == CLASS_TYPE_PALE_MASTER) + { + nLevel += (GetLevelByClass(CLASS_TYPE_PALE_MASTER, oCreature) + 1) / 2; + } + } + } + return nLevel; +} int ai_GetCanCastSpell(object oCreature, int nSpell, int nClass, int nLevel, int nMetaMagic = 0, int nDomain = 0) { int nIndex, nSpellCount, nClassPosition, nSlot, nMaxSlots, nPosition = 1; @@ -375,73 +402,198 @@ int ai_GetIsSpellBookRestrictedCaster(object oAssociate) } return FALSE; } +// This is used to set immunities on a creature not using the AI. +// Should only update every minute. +void ai_SetCreatureItemImmunities(object oCreature) +{ + // Create an Immunity in json so we can check item immunities quickly for non-AI creatures. + SetLocalInt(oCreature, sIPTimeStampVarname, ai_GetCurrentTimeStamp()); + if(AI_DEBUG) ai_Debug("0i_spells", "402", "Checking for Item immunities on " + GetName(oCreature)); + int nSpellImmunity, bHasItemImmunity, nSlot; + json jImmunity = JsonArray(); + DeleteLocalInt(oCreature, sIPImmuneVarname); + DeleteLocalInt(oCreature, sIPResistVarname); + DeleteLocalInt(oCreature, sIPReducedVarname); + int nIprpSubType, nSpell, nLevel, nIPType, nIndex; + itemproperty ipProp; + // Cycle through all the creatures equiped items. + object oItem = GetItemInSlot(nSlot, oCreature); + while(nSlot < 12) + { + if(oItem != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_spells", "416", "Checking Item immunities on " + GetName(oItem)); + ipProp = GetFirstItemProperty(oItem); + // Check for immunities on items. + while(GetIsItemPropertyValid(ipProp)) + { + nIPType = GetItemPropertyType(ipProp); + if(AI_DEBUG) ai_Debug("0i_spells", "422", "ItempropertyType(53/20/23/22): " + IntToString(nIPType)); + if(nIPType == ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL) + { + bHasItemImmunity = TRUE; + nSpellImmunity = GetItemPropertyCostTableValue(ipProp); + nSpellImmunity = StringToInt(Get2DAString("iprp_spellcost", "SpellIndex", nSpellImmunity)); + //if(AI_DEBUG) ai_Debug("0i_talents", "1950", "SpellImmunity to " + Get2DAString("spells", "Label", nSpellImmunity)); + jImmunity = JsonArrayInsert(jImmunity, JsonInt(nSpellImmunity)); + } + else if(nIPType == ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE) + { + int nBit, nIpSubType = GetItemPropertySubType(ipProp); + if(AI_DEBUG) ai_Debug("0i_talents", "434", "Immune DmgType: nIPSubType: " + IntToString(nIpSubType)); + if(nIpSubType == 0) nBit = DAMAGE_TYPE_BLUDGEONING; + else if(nIpSubType == 1) nBit = DAMAGE_TYPE_PIERCING; + else if(nIpSubType == 2) nBit = DAMAGE_TYPE_SLASHING; + else if(nIpSubType == 5) nBit = DAMAGE_TYPE_MAGICAL; + else if(nIpSubType == 6) nBit = DAMAGE_TYPE_ACID; + else if(nIpSubType == 7) nBit = DAMAGE_TYPE_COLD; + else if(nIpSubType == 8) nBit = DAMAGE_TYPE_DIVINE; + else if(nIpSubType == 9) nBit = DAMAGE_TYPE_ELECTRICAL; + else if(nIpSubType == 10) nBit = DAMAGE_TYPE_FIRE; + else if(nIpSubType == 11) nBit = DAMAGE_TYPE_NEGATIVE; + else if(nIpSubType == 12) nBit = DAMAGE_TYPE_POSITIVE; + else if(nIpSubType == 13) nBit = DAMAGE_TYPE_SONIC; + if(nBit > 0) ai_SetItemProperty(oCreature, sIPImmuneVarname, nBit, TRUE); + } + else if(nIPType == ITEM_PROPERTY_DAMAGE_RESISTANCE) + { + int nBit, nIpSubType = GetItemPropertySubType(ipProp); + if(AI_DEBUG) ai_Debug("0i_talents", "452", "Dmg Resist: nIPSubType: " + IntToString(nIpSubType)); + if(nIpSubType == 0) nBit = DAMAGE_TYPE_BLUDGEONING; + else if(nIpSubType == 1) nBit = DAMAGE_TYPE_PIERCING; + else if(nIpSubType == 2) nBit = DAMAGE_TYPE_SLASHING; + else if(nIpSubType == 5) nBit = DAMAGE_TYPE_MAGICAL; + else if(nIpSubType == 6) nBit = DAMAGE_TYPE_ACID; + else if(nIpSubType == 7) nBit = DAMAGE_TYPE_COLD; + else if(nIpSubType == 8) nBit = DAMAGE_TYPE_DIVINE; + else if(nIpSubType == 9) nBit = DAMAGE_TYPE_ELECTRICAL; + else if(nIpSubType == 10) nBit = DAMAGE_TYPE_FIRE; + else if(nIpSubType == 11) nBit = DAMAGE_TYPE_NEGATIVE; + else if(nIpSubType == 12) nBit = DAMAGE_TYPE_POSITIVE; + else if(nIpSubType == 13) nBit = DAMAGE_TYPE_SONIC; + if(nBit > 0) ai_SetItemProperty(oCreature, sIPResistVarname, nBit, TRUE); + } + else if(nIPType == ITEM_PROPERTY_DAMAGE_REDUCTION) + { + int nIpSubType = GetItemPropertySubType(ipProp); + if(AI_DEBUG) ai_Debug("0i_talents", "470", "Dmg Reduction: nIPSubType: " + IntToString(nIpSubType)); + SetLocalInt(oCreature, sIPReducedVarname, nIpSubType); + } + nIndex++; + ipProp = GetNextItemProperty(oItem); + } + // If nSpellImmunity has been set then we need to save our Immunity json. + if(bHasItemImmunity) SetLocalJson(oCreature, AI_TALENT_IMMUNITY, jImmunity); + } + oItem = GetItemInSlot(++nSlot, oCreature); + // Make the final check the creatures hide. + if(nSlot == 11) oItem = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature); + } +} int ai_CreatureImmuneToEffect(object oCaster, object oCreature, int nSpell) { + // This checks for creatures not using the AI system (usually players). + // Creatures using the AI system will always have a value in sIPReducedVarname! + // Updates thier immunity values every minute. Should be good as we only update + // equiped items. Spell effects are checked on the creature and are not saved. + if(AI_DEBUG) + { + if(GetLocalInt(oCreature, sIPReducedVarname) == 0) ai_Debug("0i_spells", "492", + " Immunities last saved: " + IntToString(GetLocalInt(oCreature, sIPTimeStampVarname)) + + " + 60 < " + IntToString(ai_GetCurrentTimeStamp())); + } + if(GetLocalInt(oCreature, sIPReducedVarname) == 0 && + GetLocalInt(oCreature, sIPTimeStampVarname) + 60 < ai_GetCurrentTimeStamp()) ai_SetCreatureItemImmunities(oCreature); string sIType = Get2DAString("ai_spells", "ImmunityType", nSpell); + // Let us check if the creature is disabled while we look for immunities. + int nDisabled = ai_Disabled(oCreature); + if(AI_DEBUG) ai_Debug("0i_spells", "499", "Checking spell immunity type(" + sIType + ")."); if(sIType != "") { - if(AI_DEBUG) ai_Debug("0i_spells", "290", "Checking spell immunity type(" + sIType + ")."); if(sIType == "Death" && GetIsImmune(oCreature, IMMUNITY_TYPE_DEATH)) return TRUE; else if(sIType == "Level_Drain" && GetIsImmune(oCreature, IMMUNITY_TYPE_NEGATIVE_LEVEL)) return TRUE; else if(sIType == "Ability_Drain" && GetIsImmune(oCreature, IMMUNITY_TYPE_ABILITY_DECREASE)) return TRUE; else if(sIType == "Poison" && GetIsImmune(oCreature, IMMUNITY_TYPE_POISON)) return TRUE; else if(sIType == "Disease" && GetIsImmune(oCreature, IMMUNITY_TYPE_DISEASE)) return TRUE; else if(sIType == "Curse" && GetIsImmune(oCreature, IMMUNITY_TYPE_CURSED)) return TRUE; - else if(sIType == "Mind_Affecting" && GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS)) return TRUE; - else if(sIType == "Petrification" && ai_IsImmuneToPetrification(oCaster, oCreature)) return TRUE; + else if(sIType == "Mind_Affecting" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; + else if(sIType == "Petrification" && + (ai_IsImmuneToPetrification(oCaster, oCreature) && nDisabled)) return TRUE; else if(sIType == "Fear" && (GetIsImmune(oCreature, IMMUNITY_TYPE_FEAR) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; else if(sIType == "Sleep" && (GetIsImmune(oCreature, IMMUNITY_TYPE_SLEEP) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; else if(sIType == "Paralysis" && (GetIsImmune(oCreature, IMMUNITY_TYPE_PARALYSIS) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; else if(sIType == "Domination" && (GetIsImmune(oCreature, IMMUNITY_TYPE_DOMINATE) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; else if(sIType == "Confusion" && (GetIsImmune(oCreature, IMMUNITY_TYPE_CONFUSED) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; else if(sIType == "Blindness" && (GetIsImmune(oCreature, IMMUNITY_TYPE_BLINDNESS) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || ai_GetHasEffectType(oCreature, EFFECT_TYPE_BLINDNESS))) return TRUE; else if(sIType == "Dazed" && (GetIsImmune(oCreature, IMMUNITY_TYPE_DAZED) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; else if(sIType == "Charm" && (GetIsImmune(oCreature, IMMUNITY_TYPE_CHARM) || - GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS) || nDisabled)) return TRUE; // Check for damage immunities. // Negative damage does not work on undead! else if(sIType == "Negative" && GetRacialType(oCreature) == RACIAL_TYPE_UNDEAD) { - if(AI_DEBUG) ai_Debug("0i_spell", "325", "Undead are immune to Negative energy!"); + if(AI_DEBUG) ai_Debug("0i_spell", "538", "Undead are immune to Negative energy!"); return TRUE; } + else if(sIType == "Poison" && ai_GetHasEffectType(oCreature, EFFECT_TYPE_POISON)) return TRUE; + else if(sIType == "Disease" && ai_GetHasEffectType(oCreature, EFFECT_TYPE_DISEASE)) return TRUE; // Elemental damage resistances should be checked. if(sIType == "Acid" || sIType == "Cold" || sIType == "Fire" || sIType == "Electricty" || sIType == "Sonic") { if(ai_GetHasEffectType(oCreature, EFFECT_TYPE_DAMAGE_RESISTANCE)) { - if(AI_DEBUG) ai_Debug("0i_spell", "334", GetName(oCreature) + " has damage resistance to my " + sIType + " spell!"); + if(AI_DEBUG) ai_Debug("0i_spell", "547", GetName(oCreature) + " has damage resistance to my " + sIType + " spell!"); return TRUE; } // Check for resistances and immunities. Treat resistance as immune. int nIPResist = GetLocalInt(oCreature, sIPResistVarname); - if(AI_DEBUG) ai_Debug("0i_spell", "372", "nIPResist:" + IntToString(nIPResist)); + if(AI_DEBUG) ai_Debug("0i_spell", "552", "nIPResist:" + IntToString(nIPResist)); int nIPImmune = GetLocalInt(oCreature, sIPImmuneVarname) | nIPResist; - if(AI_DEBUG) ai_Debug("0i_spell", "374", "nIPImmune:" + IntToString(nIPImmune)); - if(nIPImmune > 0) + if(AI_DEBUG) ai_Debug("0i_spell", "554", "nIPImmune:" + IntToString(nIPImmune)); + int bImmune; + if(nIPImmune > 0) { + + if(sIType == "Acid" && (nIPImmune & DAMAGE_TYPE_ACID)) bImmune = TRUE; + else if(sIType == "Cold" && (nIPImmune & DAMAGE_TYPE_COLD)) bImmune = TRUE; + else if(sIType == "Fire" && (nIPImmune & DAMAGE_TYPE_FIRE)) bImmune = TRUE; + else if(sIType == "Electricity" && (nIPImmune & DAMAGE_TYPE_ELECTRICAL)) bImmune = TRUE; + else if(sIType == "Sonic" && (nIPImmune & DAMAGE_TYPE_SONIC)) bImmune = TRUE; + } + if(bImmune) { - if(AI_DEBUG) ai_Debug("0i_spell", "391", GetName(oCreature) + " is immune/resistant to my " + sIType + " spell through an item!"); - if(sIType == "Acid" && (nIPImmune & DAMAGE_TYPE_ACID)) return TRUE; - else if(sIType == "Cold" && (nIPImmune & DAMAGE_TYPE_COLD)) return TRUE; - else if(sIType == "Fire" && (nIPImmune & DAMAGE_TYPE_FIRE)) return TRUE; - else if(sIType == "Electricity" && (nIPImmune & DAMAGE_TYPE_ELECTRICAL)) return TRUE; - else if(sIType == "Sonic" && (nIPImmune & DAMAGE_TYPE_SONIC)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_spell", "567", GetName(oCreature) + " is immune/resistant to my " + sIType + " spell through an item!"); + return TRUE; + } + } + // Lets also check undead and constructs vs mind spells. + int nRace = GetRacialType(oCreature); + int nClass = GetClassByPosition(1, oCreature); + if(nRace == RACIAL_TYPE_UNDEAD || nRace == RACIAL_TYPE_CONSTRUCT || + nClass == CLASS_TYPE_UNDEAD || nClass == CLASS_TYPE_CONSTRUCT) + { + if(sIType == "Mind_Affecting" || sIType == "Fear" || sIType == "Sleep" || + sIType == "Confusion" || sIType == "Blindness" || sIType == "Daze" || + sIType == "Poison" || sIType == "Disease" || sIType == "Charm") + { + if(AI_DEBUG) ai_Debug("0i_spell", "595", GetName(oCreature) + " is immune/resistant to my " + sIType + " spell because they are Undead or a Construct!"); + return TRUE; } } } @@ -460,12 +612,12 @@ int ai_CreatureImmuneToEffect(object oCaster, object oCreature, int nSpell) { if(nSpell == JsonGetInt(jSpell)) { - if(AI_DEBUG) ai_Debug("0i_spells", "407", GetName(oCreature) + " is immune to the spell via an Item!"); + if(AI_DEBUG) ai_Debug("0i_spells", "581", GetName(oCreature) + " is immune to the spell via an Item!"); return TRUE; } jSpell = JsonArrayGet(jSpellImmunity, ++nIndex); } - if(AI_DEBUG) ai_Debug("0i_spell", "347", GetName(oCreature) + " is not immune to the spell."); + if(AI_DEBUG) ai_Debug("0i_spell", "586", GetName(oCreature) + " is not immune to the spell."); return FALSE; } float ai_GetSpellRange(int nSpell) @@ -632,9 +784,9 @@ int ai_IsSilenced(object oCreature, int nSpell) } int ai_ArcaneSpellFailureTooHigh(object oCreature, int nClass, int nLevel, int nSlot) { - if(AI_DEBUG) ai_Debug("0i_spells", "561", "Arcane Spells: " + Get2DAString("classes", "ASF", nClass) + + if(AI_DEBUG) ai_Debug("0i_spells", "746", "Arcane Spells: " + Get2DAString("classes", "ASF", nClass) + " Arcane Spell Failure: " + IntToString(GetArcaneSpellFailure(oCreature)) + - " AI_ASF_WILL_USE: " + IntToString(AI_ASF_WILL_USE)); + " > " + IntToString(AI_ASF_WILL_USE) + " skip."); if(Get2DAString("classes", "ASF", nClass) == "1" && GetArcaneSpellFailure(oCreature) > AI_ASF_WILL_USE) { @@ -1162,7 +1314,7 @@ int ai_CheckAndCastSpell(object oCaster, int nSpell, int nSpellGroup, float fDel // Search all memorized spells for the spell. if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") { - // Check each level starting with the highest to lowest. + // Check each level starting with the lowest to the highest. nSpellLevel = 0; while(nSpellLevel < 10) { @@ -1174,6 +1326,8 @@ int ai_CheckAndCastSpell(object oCaster, int nSpell, int nSpellGroup, float fDel if(GetMemorizedSpellReady(oCaster, nClass, nSpellLevel, nSpellSlot)) { nMemorizedSpell = GetMemorizedSpellId(oCaster, nClass, nSpellLevel, nSpellSlot); + if(AI_DEBUG) ai_Debug("0i_spells", "1326", "nMemorizedSpell: " + IntToString(nMemorizedSpell) + + " nSpell: " + IntToString(nSpell)); if(nMemorizedSpell == nSpell) { ai_CastMemorizedSpell(oCaster, nClass, nSpellLevel, nSpellSlot, oTarget, FALSE, oPC); @@ -1412,7 +1566,7 @@ void ai_CheckForPerDayItems(object oCreature, object oPC, int nBuffType) nCntr++; } } -void ai_CheckForBuffSpells(struct stSpell stSpell) +void ai_CheckForBuffSpells(struct stSpell stSpell, float fDelay, int bInstantSpell) { ai_SetupAllyTargets(stSpell.oCaster, stSpell.oPC); stSpell.nPosition = 1; @@ -1433,13 +1587,13 @@ void ai_CheckForBuffSpells(struct stSpell stSpell) if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell, fDelay, bInstantSpell)); return; } else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell, fDelay, bInstantSpell)); return; } } @@ -1447,7 +1601,7 @@ void ai_CheckForBuffSpells(struct stSpell stSpell) } ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); } -void ai_ActionCastMemorizedSummons(struct stSpell stSpell) +void ai_ActionCastMemorizedSummons(struct stSpell stSpell, float fDelay, int bInstantSpell) { if(AI_DEBUG) ai_Debug("0i_spells", "1122", "Start of ActionCastMemorizedSummons!"); int nSpell; @@ -1473,14 +1627,14 @@ void ai_ActionCastMemorizedSummons(struct stSpell stSpell) if(Get2DAString("ai_spells", "Category", nSpell) == "S") { SetLocalInt(stSpell.oCaster, "AI_USED_SPELL_GROUP_-2", TRUE); - ai_CastMemorizedSpell(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot, stSpell.oCaster, TRUE, stSpell.oPC); + ai_CastMemorizedSpell(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot, stSpell.oCaster, bInstantSpell, stSpell.oPC); stSpell.nPosition = 1; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; DelayCommand(2.0, ai_SetupAllyTargets(stSpell.oCaster, stSpell.oPC)); - DelayCommand(2.0 + 0.5, AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell))); + DelayCommand(2.0 + 0.5, AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell, fDelay, bInstantSpell))); return; } } @@ -1510,14 +1664,14 @@ void ai_ActionCastMemorizedSummons(struct stSpell stSpell) else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell, fDelay, bInstantSpell)); return; } } } - ai_CheckForBuffSpells(stSpell); + ai_CheckForBuffSpells(stSpell, fDelay, bInstantSpell); } -void ai_ActionCastKnownSummons(struct stSpell stSpell) +void ai_ActionCastKnownSummons(struct stSpell stSpell, float fDelay, int bInstantSpell) { //ai_Debug("0i_spells", "1184", "Start of ActionCastKnownSummons!"); int nSpell; @@ -1545,14 +1699,14 @@ void ai_ActionCastKnownSummons(struct stSpell stSpell) { SetLocalInt(stSpell.oCaster, "AI_USED_SPELL_GROUP_S", TRUE); //ai_Debug("0i_spells", "1209", "nSpell: " + IntToString(nSpell)); - ai_CastKnownSpell(stSpell.oCaster, stSpell.nClass, nSpell, stSpell.oCaster, TRUE, stSpell.oPC); + ai_CastKnownSpell(stSpell.oCaster, stSpell.nClass, nSpell, stSpell.oCaster, bInstantSpell, stSpell.oPC); stSpell.nPosition = 1; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; - stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; ai_SetupAllyTargets(stSpell.oCaster, stSpell.oPC); - DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell))); + DelayCommand(fDelay, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell, fDelay, bInstantSpell))); return; } } @@ -1579,15 +1733,15 @@ void ai_ActionCastKnownSummons(struct stSpell stSpell) if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell, fDelay, bInstantSpell)); return; } else stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); } } - ai_CheckForBuffSpells(stSpell); + ai_CheckForBuffSpells(stSpell, fDelay, bInstantSpell); } -void ai_ActionCastMemorizedBuff(struct stSpell stSpell) +void ai_ActionCastMemorizedBuff(struct stSpell stSpell, float fDelay, int bInstantSpell) { int nSpell; string sBuffGroup, sBuffTarget; @@ -1630,9 +1784,9 @@ void ai_ActionCastMemorizedBuff(struct stSpell stSpell) " oTarget: " + GetName(oTarget)); if(oTarget != OBJECT_INVALID) { - ai_CastMemorizedSpell(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot, oTarget, TRUE, stSpell.oPC); + ai_CastMemorizedSpell(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot, oTarget, bInstantSpell, stSpell.oPC); stSpell.nSlot++; - DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell))); + DelayCommand(fDelay, AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell, fDelay, bInstantSpell))); return; } } @@ -1663,14 +1817,14 @@ void ai_ActionCastMemorizedBuff(struct stSpell stSpell) else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell, fDelay, bInstantSpell)); return; } } } ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); } -void ai_ActionCastKnownBuff(struct stSpell stSpell) +void ai_ActionCastKnownBuff(struct stSpell stSpell, float fDelay, int bInstantSpell) { int nSpell; string sBuffGroup, sBuffTarget; @@ -1715,9 +1869,9 @@ void ai_ActionCastKnownBuff(struct stSpell stSpell) // " oTarget: " + GetName(oTarget)); if(oTarget != OBJECT_INVALID) { - ai_CastKnownSpell(stSpell.oCaster, stSpell.nClass, nSpell, oTarget, TRUE, stSpell.oPC); + ai_CastKnownSpell(stSpell.oCaster, stSpell.nClass, nSpell, oTarget, bInstantSpell, stSpell.oPC); stSpell.nSlot++; - DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell))); + DelayCommand(fDelay, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell, fDelay, bInstantSpell))); return; } } @@ -1745,7 +1899,7 @@ void ai_ActionCastKnownBuff(struct stSpell stSpell) if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell, fDelay, bInstantSpell)); return; } else stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); @@ -1767,8 +1921,12 @@ void ai_CastBuffs(object oCaster, int nBuffType, int nTarget, object oPC) stSpell.nBuffType = nBuffType; stSpell.nTarget = nTarget; stSpell.nPosition = 1; + float fDelay = GetLocalFloat(stSpell.oCaster, AI_DELAY_BUFF_CASTING); + int bInstantSpell; + if(fDelay < 4.9) bInstantSpell = TRUE; + else fDelay = 6.0; // Look for summons spells on All, Long durations and the whole party. - if((nBuffType == 1 || nBuffType == 3) && nTarget == 0) + if((nBuffType == 1 || nBuffType == 3) && nTarget == 0 && GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster) == OBJECT_INVALID) { while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { @@ -1783,13 +1941,13 @@ void ai_CastBuffs(object oCaster, int nBuffType, int nTarget, object oPC) if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedSummons(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedSummons(stSpell, fDelay, bInstantSpell)); return; } else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); - AssignCommand(stSpell.oCaster, ai_ActionCastKnownSummons(stSpell)); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownSummons(stSpell, fDelay, bInstantSpell)); return; } } @@ -1798,7 +1956,7 @@ void ai_CastBuffs(object oCaster, int nBuffType, int nTarget, object oPC) // Exit here; if we summoned a monster then it linked off of that spell // cast to continue the action queue for all buff spell cast actions. } - ai_CheckForBuffSpells(stSpell); + ai_CheckForBuffSpells(stSpell, fDelay, bInstantSpell); } int ai_CastSpontaneousCure(object oCreature, object oTarget, object oPC) { @@ -1864,6 +2022,98 @@ int ai_CastSpontaneousCure(object oCreature, object oTarget, object oPC) } return FALSE; } +int ai_CastMemorizedInflict(object oCreature, object oTarget, object oPC, int nClass) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1993", GetName(oCreature) + " is looking to cast a memorized inflict spell."); + int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); + int nSpell, nSlot, nMaxSlots, nLevel = 9; + int nClassSave, nSlotSave, nLevelSave = 10; + while(nLevel > -1) + { + nMaxSlots = GetMemorizedSpellCountByLevel(oCreature, nClass, nLevel); + nSlot = 0; + if(AI_DEBUG) ai_Debug("0i_spells", "2001", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); + while(nSlot < nMaxSlots) + { + if(AI_DEBUG) ai_Debug("0i_spells", "2004", "nSlot: " + IntToString(nSlot) + + " Spell Ready: " + IntToString(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot))); + if(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot)) + { + nSpell = GetMemorizedSpellId(oCreature, nClass, nLevel, nSlot); + if(ai_ShouldWeCastThisInflictSpell(nSpell, nDamage)) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(AI_DEBUG) ai_Debug("0i_spells", "2012", GetName(oCreature) + " has cast " + sSpellName + " on " + GetName(oTarget) + "."); + ai_CastMemorizedSpell(oCreature, nClass, nLevel, nSlot, oTarget, FALSE, oPC); + return TRUE; + } + // Save the lowest level inflict spell as we might need to cast it. + else if(nLevel < nLevelSave && (nSpell > 430 && nSpell < 436)) + { + nClassSave = nClass; + nLevelSave = nLevel; + nSlotSave = nSlot; + } + } + nSlot++; + } + nLevel--; + } + // Did we find a cure spell? If we did then use it. + if(nLevelSave < 10) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1740", GetName(oCreature) + " has cast the lowest level cure spell on " + GetName(oTarget) + "."); + ai_CastMemorizedSpell(oCreature, nClassSave, nLevelSave, nSlotSave, oTarget, FALSE, oPC); + return TRUE; + } + return FALSE; +} +int ai_CastKnownInflict(object oCreature, object oTarget, object oPC, int nClass) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "2041", GetName(oCreature) + " is looking to cast a known inflict spell."); + int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); + int nSpell, nSlot, nMaxSlots, nLevel = 9; + int nClassSave, nSpellSave, nLevelSave = 10; + while(nLevel > -1) + { + nMaxSlots = GetKnownSpellCount(oCreature, nClass, nLevel); + nSlot = 0; + if(AI_DEBUG) ai_Debug("0i_spells", "2049", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); + while(nSlot < nMaxSlots) + { + nSpell = GetKnownSpellId(oCreature, nClass, nLevel, nSlot); + if(AI_DEBUG) ai_Debug("0i_spells", "2053", "nSlot: " + IntToString(nSlot) + + " Spell Ready: " + IntToString(GetSpellUsesLeft(oCreature, nClass, nSpell))); + if(GetSpellUsesLeft(oCreature, nClass, nSpell)) + { + if(ai_ShouldWeCastThisInflictSpell(nSpell, nDamage)) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(AI_DEBUG) ai_Debug("0i_spells", "2060", GetName(oCreature) + " has cast " + sSpellName + " on " + GetName(oTarget) + "."); + ai_CastKnownSpell(oCreature, nClass, nSpell, oTarget, FALSE, oPC); + return TRUE; + } + // Save the lowest level cure spell as we might need to cast it. + else if(nLevel < nLevelSave && (nSpell > 430 && nSpell < 436)) + { + nClassSave = nClass; + nLevelSave = nLevel; + nSpellSave = nSpell; + } + } + nSlot++; + } + nLevel--; + } + return FALSE; + // Did we find a cure spell? If we did then use it. + if(nLevelSave < 10) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1781", GetName(oCreature) + " has cast the lowest level cure spell on " + GetName(oTarget) + "."); + ai_CastKnownSpell(oCreature, nClassSave, nSpellSave, oTarget, FALSE, oPC); + return TRUE; + } +} int ai_CastMemorizedHealing(object oCreature, object oTarget, object oPC, int nClass) { if(AI_DEBUG) ai_Debug("0i_spells", "1702", GetName(oCreature) + " is looking to cast a memorized cure spell."); @@ -1890,7 +2140,7 @@ int ai_CastMemorizedHealing(object oCreature, object oTarget, object oPC, int nC return TRUE; } // Save the lowest level cure spell as we might need to cast it. - else if(nLevel < nLevelSave && (nSpell > 26 && nSpell < 32)) + else if(nLevel < nLevelSave && (nSpell > 30 && nSpell < 36)) { nClassSave = nClass; nLevelSave = nLevel; @@ -1936,7 +2186,7 @@ int ai_CastKnownHealing(object oCreature, object oTarget, object oPC, int nClass return TRUE; } // Save the lowest level cure spell as we might need to cast it. - else if(nLevel < nLevelSave && (nSpell > 26 && nSpell < 32)) + else if(nLevel < nLevelSave && (nSpell > 30 && nSpell < 36)) { nClassSave = nClass; nLevelSave = nLevel; @@ -2055,7 +2305,7 @@ float ai_GetOffensiveSpellSearchRange(object oCreature, int nSpell) // We check this because if the enemy is moving or has not started acting // then we don't want to move up on them as they might move towards us! int nAction = GetCurrentAction(oNearestEnemy); - if(AI_DEBUG) ai_Debug("0i_spells", "1130", GetName(oNearestEnemy) + " current action: " + IntToString(nAction)); + if(AI_DEBUG) ai_Debug("0i_spells", "2228", GetName(oNearestEnemy) + " current action: " + IntToString(nAction)); if(nAction != ACTION_MOVETOPOINT || nAction != ACTION_ITEMCASTSPELL || nAction != ACTION_INVALID || nAction != ACTION_USEOBJECT || nAction != ACTION_RANDOMWALK) fRange = fEnemyDistance + (fRange - 7.5); @@ -2064,6 +2314,18 @@ float ai_GetOffensiveSpellSearchRange(object oCreature, int nSpell) else if(fRange < 0.1f) return 0.1f; return fRange; } +int ai_ShouldWeCastThisInflictSpell(int nSpell, int nDamage) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "2239", "nSpell: " + IntToString(nSpell) + " nDamage: " + + IntToString(nDamage)); + if(nSpell == SPELL_HEAL && nDamage > 50) return TRUE; + else if(nSpell == SPELL_INFLICT_CRITICAL_WOUNDS && nDamage > 31) return TRUE; + else if(nSpell == SPELL_INFLICT_SERIOUS_WOUNDS && nDamage > 23) return TRUE; + else if(nSpell == SPELL_INFLICT_MODERATE_WOUNDS && nDamage > 15) return TRUE; + else if(nSpell == SPELL_INFLICT_LIGHT_WOUNDS && nDamage > 6) return TRUE; + else if(nSpell == SPELL_INFLICT_MINOR_WOUNDS) return TRUE; + return FALSE; +} int ai_ShouldWeCastThisCureSpell(int nSpell, int nDamage) { if(AI_DEBUG) ai_Debug("0i_spells", "1127", "nSpell: " + IntToString(nSpell) + " nDamage: " + @@ -2093,7 +2355,7 @@ void ai_CastWidgetSpell(object oPC, object oAssociate, object oTarget, location // " oTarget: " + GetName(oTarget) + // " nMetaMagic: " + IntToString(nMetaMagic) + // " nDomain: " + IntToString(nDomain)); - if(GetCurrentAction(oAssociate) != ACTION_CASTSPELL) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); + if(GetCurrentAction(oAssociate) != ACTION_CASTSPELL) AssignCommand(oAssociate, ai_ClearCreatureActions(FALSE)); if(!GetIsObjectValid(oTarget)) { AssignCommand(oAssociate, ActionCastSpellAtLocation(nSpell, lLocation, nMetaMagic, FALSE, 0, FALSE, -1, FALSE, nDomain)); @@ -2111,13 +2373,17 @@ void ai_UseWidgetFeat(object oPC, object oAssociate, object oTarget, location lL json jWidget = JsonArrayGet(jSpells, 2); json jFeat = JsonArrayGet(jWidget, nIndex); int nFeat = JsonGetInt(JsonArrayGet(jFeat, 5)); - if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); + int nLevel = JsonGetInt(JsonArrayGet(jFeat, 2)); + // We use nLevel at -1 to denote this is a feat with a subradial spell. + int nSubSpell; + if(nLevel == -1) nSubSpell = JsonGetInt(JsonArrayGet(jFeat, 0)); + if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(FALSE)); //SendMessageToPC(oPC, "0i_spells, 2104, nFeat: " + IntToString(nFeat) + " oTarget: " + GetName(oTarget)); if(!GetIsObjectValid(oTarget)) { - AssignCommand(oAssociate, ActionUseFeat(nFeat, OBJECT_INVALID, 0, lLocation)); + AssignCommand(oAssociate, ActionUseFeat(nFeat, OBJECT_INVALID, nSubSpell, lLocation)); } - else AssignCommand(oAssociate, ActionUseFeat(nFeat, oTarget)); + else AssignCommand(oAssociate, ActionUseFeat(nFeat, oTarget, nSubSpell)); } void ai_UseWidgetItem(object oPC, object oAssociate, object oTarget, location lLocation) { @@ -2132,7 +2398,7 @@ void ai_UseWidgetItem(object oPC, object oAssociate, object oTarget, location lL int nIprpSubType = JsonGetInt(JsonArrayGet(jItem, 4)); object oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jItem, 5))); itemproperty ipProperty; - if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); + if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(FALSE)); if(nSpell == SPELL_HEALINGKIT) { ipProperty = GetFirstItemProperty(oItem); @@ -2149,9 +2415,9 @@ void ai_UseWidgetItem(object oPC, object oAssociate, object oTarget, location lL if(nIprpSubType == GetItemPropertySubType(ipProperty)) break; ipProperty = GetNextItemProperty(oItem); } - if(!GetIsObjectValid(oTarget)) + if(GetIsObjectValid(oTarget)) { - AssignCommand(oAssociate, ActionUseItemAtLocation(oItem, ipProperty, lLocation)); + AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oTarget)); } - else AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oTarget)); + else AssignCommand(oAssociate, ActionUseItemAtLocation(oItem, ipProperty, lLocation)); } diff --git a/src/module/nss/0i_states_cond.nss b/src/module/nss/0i_states_cond.nss index 4c77ccf..b1a71d3 100644 --- a/src/module/nss/0i_states_cond.nss +++ b/src/module/nss/0i_states_cond.nss @@ -94,6 +94,7 @@ void ai_ClearCreatureActions(int bClearCombatState = FALSE) void ai_SetLastAction(object oCreature, int nAction = AI_LAST_ACTION_NONE) { SetLocalInt(oCreature, sLastActionVarname, nAction); + SetLocalInt(oCreature, sLastActionTimeVarname, ai_GetCurrentTimeStamp()); } int ai_CompareLastAction(object oCreature, int nAction) { @@ -105,6 +106,7 @@ int ai_CompareLastAction(object oCreature, int nAction) } void ai_SetListeningPatterns(object oCreature) { + SetListening(oCreature, TRUE); SetListenPattern(oCreature, AI_I_SEE_AN_ENEMY, AI_ALLY_SEES_AN_ENEMY); SetListenPattern(oCreature, AI_I_HEARD_AN_ENEMY, AI_ALLY_HEARD_AN_ENEMY); SetListenPattern(oCreature, AI_ATKED_BY_WEAPON, AI_ALLY_ATKED_BY_WEAPON); @@ -114,7 +116,6 @@ void ai_SetListeningPatterns(object oCreature) SetListenPattern(oCreature, AI_I_AM_DISEASED, AI_ALLY_IS_DISEASED); SetListenPattern(oCreature, AI_I_AM_POISONED, AI_ALLY_IS_POISONED); SetListenPattern(oCreature, AI_I_AM_WEAK, AI_ALLY_IS_WEAK); - SetListening(oCreature, TRUE); } int ai_IsNonliving(int nRacialType) { @@ -134,12 +135,12 @@ int ai_GetIsInCombat(object oCreature) } void ai_SetCombatRound(object oCreature) { - SetLocalInt(oCreature, "AI_COMBAT_ROUND_START", SQLite_GetTimeStamp()); - if(AI_DEBUG) ai_Debug("0i_states_cond", "116", " ===============> " + GetName(oCreature) + " ROUND START:" + IntToString(SQLite_GetTimeStamp()) + " <==============="); + SetLocalInt(oCreature, "AI_COMBAT_ROUND_START", ai_GetCurrentTimeStamp()); + if(AI_DEBUG) ai_Debug("0i_states_cond", "116", " ===============> " + GetName(oCreature) + " ROUND START:" + IntToString(ai_GetCurrentTimeStamp()) + " <==============="); } void ai_EndCombatRound(object oCreature) { - if(AI_DEBUG) ai_Debug("0i_states_cond", "120", " ===============> " + GetName(oCreature) + " ROUND END:" + IntToString(SQLite_GetTimeStamp()) + " <==============="); + if(AI_DEBUG) ai_Debug("0i_states_cond", "120", " ===============> " + GetName(oCreature) + " ROUND END:" + IntToString(ai_GetCurrentTimeStamp()) + " <==============="); DeleteLocalInt(oCreature, "AI_COMBAT_ROUND_START"); } int ai_IsInCombatRound(object oCreature, int nCombatRound = AI_COMBAT_ROUND_IN_SECONDS) @@ -148,10 +149,10 @@ int ai_IsInCombatRound(object oCreature, int nCombatRound = AI_COMBAT_ROUND_IN_S if(AI_DEBUG) ai_Debug("0i_states_cond", "148", " nCombatRoundStart: " + IntToString(nCombatRoundStart)); if(!nCombatRoundStart) return FALSE; // New combat round calculator. If 6 seconds has passed then we are on a new round! - int nSQLTime = SQLite_GetTimeStamp(); - int nCombatRoundTime = nSQLTime - nCombatRoundStart; - if(AI_DEBUG) ai_Debug("0i_states_cond", "153", " SQLite_GetTimeStamp: " + IntToString(nSQLTime) + - "(" + IntToString(nSQLTime - nCombatRoundStart) + ")"); + int nTime = ai_GetCurrentTimeStamp(); + int nCombatRoundTime = nTime - nCombatRoundStart; + if(AI_DEBUG) ai_Debug("0i_states_cond", "153", " nTime + (nTime - Round Start): " + IntToString(nTime) + + "(" + IntToString(nTime - nCombatRoundStart) + ")"); if(nCombatRoundTime < nCombatRound) return TRUE; ai_EndCombatRound(oCreature); return FALSE; @@ -200,9 +201,11 @@ int ai_Disabled(object oCreature) effect eEffect = GetFirstEffect(oCreature); while(GetIsEffectValid(eEffect)) { - switch(GetEffectType(eEffect)) + switch(GetEffectType(eEffect, TRUE)) { + WriteTimestampedLogEntry("Effect Type: " + IntToString(GetEffectType(eEffect, TRUE))); case EFFECT_TYPE_DOMINATED : + case EFFECT_TYPE_CUTSCENE_DOMINATED : { if(!GetCommandable(oCreature)) SetCommandable(TRUE, oCreature); return FALSE; @@ -213,6 +216,7 @@ int ai_Disabled(object oCreature) case EFFECT_TYPE_CONFUSED : case EFFECT_TYPE_FRIGHTENED : case EFFECT_TYPE_PARALYZE : + case EFFECT_TYPE_CUTSCENE_PARALYZE : case EFFECT_TYPE_TURNED : case EFFECT_TYPE_CHARMED : case EFFECT_TYPE_PETRIFY : diff --git a/src/module/nss/0i_talents.nss b/src/module/nss/0i_talents.nss index fa773b9..693003f 100644 --- a/src/module/nss/0i_talents.nss +++ b/src/module/nss/0i_talents.nss @@ -63,8 +63,16 @@ int ai_TryTailSweepAttack(object oCreature); int ai_TrySneakAttack(object oCreature, int nInMelee, int bAlwaysAtk = TRUE); // Returns TRUE if oCreature finds a good ranged target and uses Sneak Attack. int ai_TryRangedSneakAttack(object oCreature, int nInMelee); +// Returns TRUE if oCreature uses a harmful ranged talent. +int ai_TryRangedTalents(object oCreature, object oTarget, int nInMelee); // Returns TRUE if oCreature uses a harmful melee talent. int ai_TryMeleeTalents(object oCreature, object oTarget); +//:: [PRC8] Tests & runs Charge +int ai_TryCharging(object oCreature, object oTarget); +//:: [PRC8] Tests & runs Grapple +int ai_TryGrappling(object oCreature, object oTarget); +//:: [PRC8] Tests & runs Trip +int ai_TryTripping(object oCreature, object oTarget); // ***************************************************************************** // ******************************* Try * Skills ******************************** // ***************************************************************************** @@ -132,6 +140,9 @@ int ai_TryKiDamageFeat(object oCreature, object oTarget); // Returns TRUE if oCreature uses Knockdown. // This checks if they have the feat and if its viable. int ai_TryKnockdownFeat(object oCreature, object oTarget); +// Returns the spell level value to be used to compare if they should cast a +// spell vs use Polymorph. +int ai_GetHasPolymorphSelfFeat(object oCreature); // Returns TRUE if oCreature uses a polymorph self feat. // This checks if they have the feat and will use the best one. int ai_TryPolymorphSelfFeat(object oCreature); @@ -172,14 +183,25 @@ int ai_TryWhirlwindFeat(object oCreature); // Returns TRUE if oCreature uses Wholeness of Body. // This checks if they have any uses left, have the feat and if its viable. int ai_TryWholenessOfBodyFeat(object oCreature); +//:: [PRC8] Tests & runs Awesome Blow +int ai_TryAwesomeBlowFeat(object oCreature, object oTarget); +//:: [PRC8] Tests & runs Bullrush +int ai_TryBullrushFeat(object oCreature, object oTarget); +//:: [PRC8] Tests & runs Overrun +int ai_TryOverrunFeat(object oCreature, object oTarget); +//:: [PRC8] Tests & runs Ranged Disarm +int ai_TryRangedDisarmFeat(object oCreature, object oTarget); +//:: [PRC8] Tests & runs Swarm of Arrows +int ai_TrySwarmofArrowsFeat(object oCreature); +//:: [PRC8] Tests & runs Inlindl School Strike +int ai_TryInlindlSchoolFeat(object oCreature, object oTarget); // ***************************************************************************** // ***************************** TALENT SCRIPTS ****************************** // ***************************************************************************** // These functions do not fall into another section. // Returns the MaxLevel used in GetCreatureTalent for oCreature. -// This checks the intelligence and the level of oCreature. -// Returns either -1 (random) or 10 for all talents. +// This checks the level of oCreature. int ai_GetMonsterTalentMaxLevel(object oCreature); // Returns the nMaxLevel used in GetCreatureTalent for oCreature. // This checks the difficulty of the combat and the level of oCreature. @@ -197,12 +219,13 @@ int ai_GetHasTalent(object oCreature, int nTalent); // Type 4)item 0-type, 1-spell, 2-item object, 3-level, 4-slot. // jJsonLevel is the level to place the talent in the json array // maybe different then the talents actual level which is passed in nLevel. -void ai_SaveTalent(object oCreature, int nClass, int nJsonLevel, int nLevel, int nSlot, int nSpell, int nType, int bBuff, object oItem = OBJECT_INVALID); +void ai_SaveTalent(object oCreature, int nClass, int nJsonLevel, int nLevel, int nSlot, int nSpell, int nType, int bBuff, int bPreBuff, int bFullBuff, object oItem = OBJECT_INVALID); // Removes a talent nSlotIndex from jLevel in jCategory. void ai_RemoveTalent(object oCreature, json jCategory, json jLevel, string sCategory, int nLevel, int nSlotIndex); // Saves a creatures talents to variables upon them for combat use. // bMonster will check to see if they should be buffed when we set the talents. -void ai_SetCreatureTalents(object oCreature, int bMonster); +// bForceTalentSetup - Tells the talent system to force a talent setup but no Buffing. +void ai_SetCreatureTalents(object oCreature, int bMonster, int bForceTalentSetup = FALSE); // Return TRUE if oCreature spontaneously casts a cure spell from a talent in sCategory. int ai_UseSpontaneousCureTalentFromCategory(object oCreature, string sCategory, int nInMelee, int nDamage, object oTarget = OBJECT_INVALID); // Returns TRUE if oCreature uses jTalent on oTarget. @@ -217,7 +240,8 @@ int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int n int ai_UseTalent(object oCreature, int nTalent, object oTarget); // Returns TRUE if jTalent is used on oTarget by oCaster. // Checks the talent type and casts the correct spell. For items it checks uses. -int ai_UseTalentOnObject(object oCaster, json jTalent, object oTarget, int nInMelee); +// bCheckPosition will see if we should move back due to spell failure in combat. +int ai_UseTalentOnObject(object oCaster, json jTalent, object oTarget, int nInMelee, int bCheckPosition = TRUE); // Returns TRUE if jTalent is used at lTarget location by oCaster. // Checks the talent type and cast the correct spell. For items it checks uses. int ai_UseTalentAtLocation(object oCaster, json jTalent, object oTarget, int nInMelee); @@ -259,41 +283,100 @@ int ai_TryHealingTalent(object oCreature, int nInMelee, object oTarget = OBJECT_ GetAssociateType(oTarget) > 1) return FALSE; int nHp = ai_GetPercHPLoss(oTarget); int nHpLimit = ai_GetHealersHpLimit(oCreature); - if(AI_DEBUG) ai_Debug("0i_talents", "256", "nHp: " + IntToString(nHp) + + if(AI_DEBUG) ai_Debug("0i_talents", "268", "nHp: " + IntToString(nHp) + "< nHpLimit: " + IntToString(nHpLimit)); if(nHp > nHpLimit) return FALSE; int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); - if(AI_DEBUG) ai_Debug("0i_talents", "260", GetName(oTarget) + " has lost " + IntToString(nDamage) + " hitpoints!"); - // Do they have Lay on Hands? + if(AI_DEBUG) ai_Debug("0i_talents", "272", GetName(oTarget) + " has lost " + IntToString(nDamage) + " hitpoints!"); int bUseMagic = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC); - if(bUseMagic && GetHasFeat(FEAT_LAY_ON_HANDS, oCreature)) - { - int nCanHeal = GetAbilityModifier(ABILITY_CHARISMA, oCreature) * ai_GetCharacterLevels(oCreature); - if(nCanHeal <= nDamage) - { - ai_UseFeat(oCreature, FEAT_LAY_ON_HANDS, oTarget); - return TRUE; - } - } - int nMaxLevel = 9; // If they are about to die then throw caution to the wind and HEAL! if(nHp <= AI_HEALTH_BLOODY || nHp < 11) nInMelee = 0; - if(ai_UseCreatureTalent(oCreature, AI_TALENT_HEALING, nInMelee, nMaxLevel, oTarget)) return TRUE; - if(AI_DEBUG) ai_Debug("0i_talents", "275", GetName(oCreature) + " has no healing spells!" + - " Cleric lvls: " + IntToString(GetLevelByClass(CLASS_TYPE_CLERIC, oCreature)) + - " Sontaneous casting: " + IntToString(ai_GetMagicMode(oCreature, AI_MAGIC_NO_SPONTANEOUS_CURE))); - if(bUseMagic && GetLevelByClass(CLASS_TYPE_CLERIC, oCreature) && - !ai_GetMagicMode(oCreature, AI_MAGIC_NO_SPONTANEOUS_CURE)) + int nRace = GetRacialType(oTarget); + if(nRace == RACIAL_TYPE_UNDEAD) { - // We need to check our talents and see what spells we can convert. - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_ENHANCEMENT, nInMelee, nDamage, oTarget)) return TRUE; - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_PROTECTION, nInMelee, nDamage, oTarget)) return TRUE; - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_DISCRIMINANT_AOE, nInMelee, nDamage, oTarget)) return TRUE; - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_INDISCRIMINANT_AOE, nInMelee, nDamage, oTarget)) return TRUE; - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_TOUCH, nInMelee, nDamage, oTarget)) return TRUE; - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_RANGED, nInMelee, nDamage, oTarget)) return TRUE; - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_SUMMON, nInMelee, nDamage, oTarget)) return TRUE; - if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_CURE, nInMelee, nDamage, oTarget)) return TRUE; + json jCategory = GetLocalJson(oCreature, AI_TALENT_TOUCH); + if(JsonGetType(jCategory) == JSON_TYPE_NULL) return FALSE; + int nLevel = 4, bFound, nSlotIndex, nMaxSlotIndex, nSpell; + json jLevel, jTalent; + // Loop through nLevels looking for nTalent + while(nLevel > 0) + { + // Get the array of nLevel. + jLevel = JsonArrayGet(jCategory, nLevel); + nMaxSlotIndex = JsonGetLength(jLevel); + if(nMaxSlotIndex > 0) + { + // Get the talent within nLevel cycling from the first to the last. + nSlotIndex = 0; + while (nSlotIndex < nMaxSlotIndex) + { + jTalent = JsonArrayGet(jLevel, nSlotIndex); + if(AI_DEBUG) ai_Debug("0i_talents", "296", "jTalent: " + JsonDump(jTalent, 4)); + nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); + if(nSpell == SPELL_INFLICT_CRITICAL_WOUNDS) + { + bFound = TRUE; + break; + } + else if(nSpell == SPELL_INFLICT_SERIOUS_WOUNDS) + { + bFound = TRUE; + break; + } + else if(nSpell == SPELL_INFLICT_MODERATE_WOUNDS) + { + bFound = TRUE; + break; + } + else if(nSpell == SPELL_INFLICT_LIGHT_WOUNDS) + { + bFound = TRUE; + break; + } + nSlotIndex++; + } + } + if(bFound) break; + nLevel--; + } + if(bFound) + { + if(ai_UseTalentOnObject(oCreature, jTalent, oTarget, nInMelee)) + { + ai_RemoveTalent(oCreature, jCategory, jLevel, AI_TALENT_TOUCH, nLevel, nSlotIndex); + return TRUE; + } + } + } + else + { + // Do they have Lay on Hands? + if(bUseMagic && GetHasFeat(FEAT_LAY_ON_HANDS, oCreature)) + { + int nCanHeal = GetAbilityModifier(ABILITY_CHARISMA, oCreature) * ai_GetCharacterLevels(oCreature); + if(nCanHeal <= nDamage) + { + ai_UseFeat(oCreature, FEAT_LAY_ON_HANDS, oTarget); + return TRUE; + } + } + int nMaxLevel = 9; + if(ai_UseCreatureTalent(oCreature, AI_TALENT_HEALING, nInMelee, nMaxLevel, oTarget)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "275", GetName(oCreature) + " has no healing spells!" + + " Cleric lvls: " + IntToString(GetLevelByClass(CLASS_TYPE_CLERIC, oCreature)) + + " Sontaneous casting: " + IntToString(ai_GetMagicMode(oCreature, AI_MAGIC_NO_SPONTANEOUS_CURE))); + if(bUseMagic && GetLevelByClass(CLASS_TYPE_CLERIC, oCreature) && + !ai_GetMagicMode(oCreature, AI_MAGIC_NO_SPONTANEOUS_CURE)) + { + // We need to check our talents and see what spells we can convert. + if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_ENHANCEMENT, nInMelee, nDamage, oTarget)) return TRUE; + if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_PROTECTION, nInMelee, nDamage, oTarget)) return TRUE; + if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_DISCRIMINANT_AOE, nInMelee, nDamage, oTarget)) return TRUE; + if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_INDISCRIMINANT_AOE, nInMelee, nDamage, oTarget)) return TRUE; + if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_TOUCH, nInMelee, nDamage, oTarget)) return TRUE; + if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_RANGED, nInMelee, nDamage, oTarget)) return TRUE; + if(ai_UseSpontaneousCureTalentFromCategory(oCreature, AI_TALENT_SUMMON, nInMelee, nDamage, oTarget)) return TRUE; + } } return FALSE; } @@ -347,6 +430,9 @@ int ai_CheckTargetVsConditions(object oTarget, json jTalent, int nConditions) if(ai_GetHasNegativeCondition(AI_CONDITION_ATK_DECREASE, nConditions)) return TRUE; if(ai_GetHasNegativeCondition(AI_CONDITION_DMG_DECREASE, nConditions)) return TRUE; if(ai_GetHasNegativeCondition(AI_CONDITION_DMG_I_DECREASE, nConditions)) return TRUE; + case SPELL_RAISE_DEAD : + case SPELL_RESURRECTION : + if(ai_GetHasNegativeCondition(AI_CONDITION_DEAD, nConditions)) return TRUE; } return FALSE; } @@ -354,7 +440,7 @@ int ai_CheckTalentsVsConditions(object oCreature, int nConditions, int nInMelee, { // Get the saved category from oCreature. json jCategory = GetLocalJson(oCreature, AI_TALENT_CURE); - if(AI_DEBUG) ai_Debug("0i_talents", "357", "jCategory: " + AI_TALENT_CURE + " " + JsonDump(jCategory, 2)); + if(AI_DEBUG) ai_Debug("0i_talents", "362", "jCategory: " + AI_TALENT_CURE + " " + JsonDump(jCategory, 2)); if(JsonGetType(jCategory) == JSON_TYPE_NULL) { SetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE, -1); @@ -362,7 +448,7 @@ int ai_CheckTalentsVsConditions(object oCreature, int nConditions, int nInMelee, } // Get the max talent level so we can skip the higher ones and save time. int nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE); - if(AI_DEBUG) ai_Debug("0i_talents", "365", AI_MAX_TALENT + AI_TALENT_CURE + ": " + + if(AI_DEBUG) ai_Debug("0i_talents", "370", AI_MAX_TALENT + AI_TALENT_CURE + ": " + IntToString(nMaxTalentLevel) + " nLevel: " + IntToString(nLevel)); if(nMaxTalentLevel < nLevel) nLevel = nMaxTalentLevel; @@ -371,7 +457,7 @@ int ai_CheckTalentsVsConditions(object oCreature, int nConditions, int nInMelee, int nClass, nSlot, nType, nSlotIndex, nMaxSlotIndex, nTalentUsed; int bUseMagic = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC); int bUseMagicItems = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC_ITEMS); - if(AI_DEBUG) ai_Debug("0i_talents", "374", "bUseMagic: " + IntToString(bUseMagic) + + if(AI_DEBUG) ai_Debug("0i_talents", "379", "bUseMagic: " + IntToString(bUseMagic) + " bUseMagicItems: " + IntToString(bUseMagicItems)); // Loop through nLevels down to 0 looking for the first talent (i.e. the highest). while(nLevel >= 0) @@ -379,7 +465,7 @@ int ai_CheckTalentsVsConditions(object oCreature, int nConditions, int nInMelee, // Get the array of nLevel cycling down to 0. jLevel = JsonArrayGet(jCategory, nLevel); nMaxSlotIndex = JsonGetLength(jLevel); - if(AI_DEBUG) ai_Debug("0i_talents", "382", "nLevel: " + IntToString(nLevel) + + if(AI_DEBUG) ai_Debug("0i_talents", "387", "nLevel: " + IntToString(nLevel) + " nMaxSlotIndex: " + IntToString(nMaxSlotIndex)); if(nMaxSlotIndex > 0) { @@ -388,9 +474,9 @@ int ai_CheckTalentsVsConditions(object oCreature, int nConditions, int nInMelee, while (nSlotIndex <= nMaxSlotIndex) { jTalent= JsonArrayGet(jLevel, nSlotIndex); - if(AI_DEBUG) ai_Debug("0i_talents", "391", "nSlotIndex: " + IntToString(nSlotIndex) + + if(AI_DEBUG) ai_Debug("0i_talents", "396", "nSlotIndex: " + IntToString(nSlotIndex) + " jTalent Type: " + IntToString(JsonGetType(jTalent))); - // Check to see if the talent matches oTargets nConditionss. + // Check to see if the talent matches oTargets nConditions. if(ai_CheckTargetVsConditions(oTarget, jTalent, nConditions)) { nType = JsonGetInt(JsonArrayGet(jTalent, 0)); @@ -402,12 +488,13 @@ int ai_CheckTalentsVsConditions(object oCreature, int nConditions, int nInMelee, { nTalentUsed = ai_UseCreatureSpellTalent(oCreature, jLevel, jTalent, AI_TALENT_CURE, nInMelee, oTarget); // -1 means it was a memorized spell and we need to remove it. - if(nTalentUsed == -1) - { - ai_RemoveTalent(oCreature, jCategory, jLevel, AI_TALENT_CURE, nLevel, nSlotIndex); - return TRUE; - } - else if(nTalentUsed) return TRUE; + //if(nTalentUsed == -1) + //{ + // ai_RemoveTalent(oCreature, jCategory, jLevel, AI_TALENT_CURE, nLevel, nSlotIndex); + // return TRUE; + //} + //else + if(nTalentUsed) return TRUE; } } else if(nType == AI_TALENT_TYPE_SP_ABILITY) @@ -449,14 +536,16 @@ int ai_TryCureConditionTalent(object oCreature, int nInMelee, object oTarget = O { // Is Casting Cure spells off? if(ai_GetMagicMode(oCreature, AI_MAGIC_CURE_SPELLS_OFF)) return FALSE; - if(AI_DEBUG) ai_Debug("0i_talents", "450", AI_MAX_TALENT + AI_TALENT_CURE + ": " + + if(AI_DEBUG) ai_Debug("0i_talents", "460", AI_MAX_TALENT + AI_TALENT_CURE + ": " + IntToString(GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE))); // If the creature doesn't have cure talents then we set it to -1. if(GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE) == -1) return FALSE; // We check targets to see if they need to be cured. int nNegativeConditions, nTargetNegConds, nIndex, nCnt = 1; + // Check for dead allies. object oTarget; - if(oTarget == OBJECT_INVALID) + object oDead = ai_GetNearestAlly(oCreature, 1, CREATURE_TYPE_IS_ALIVE, FALSE); + if(oDead == OBJECT_INVALID) { oTarget = GetLocalObject(oCreature, AI_ALLY + "1"); while(oTarget != OBJECT_INVALID) @@ -480,15 +569,15 @@ int ai_TryCureConditionTalent(object oCreature, int nInMelee, object oTarget = O } else { - nNegativeConditions = ai_GetNegativeConditions(oTarget); - if(!nNegativeConditions) return FALSE; + nNegativeConditions = AI_CONDITION_DEAD; + oTarget = oDead; } if(oTarget == oCreature) { if(ai_GetAIMode(oCreature, AI_MODE_SELF_HEALING_OFF)) return FALSE; } else if(ai_GetAIMode(oCreature, AI_MODE_PARTY_HEALING_OFF)) return FALSE; - if(AI_DEBUG) ai_Debug("0i_talents", "489", "nNegativeConditions: " + IntToString(nNegativeConditions) + + if(AI_DEBUG) ai_Debug("0i_talents", "501", "nNegativeConditions: " + IntToString(nNegativeConditions) + " on " + GetName(oTarget)); if(ai_CheckTalentsVsConditions(oCreature, nNegativeConditions, nInMelee, 9, oTarget)) return TRUE; return FALSE; @@ -500,7 +589,7 @@ int ai_TryCureConditionTalent(object oCreature, int nInMelee, object oTarget = O int ai_TryDefensiveTalents(object oCreature, int nInMelee, int nMaxLevel, int nRound = 0, object oTarget = OBJECT_INVALID) { - // Summons are powerfull and should be used as much as possible. + // Summons are powerful and should be used as much as possible. if(ai_UseCreatureTalent(oCreature, AI_TALENT_SUMMON, nInMelee, nMaxLevel, oTarget)) return TRUE; // Added to reduce casting defensive talents later in combat and constantly. if(nRound >= d8()) return FALSE; @@ -852,27 +941,52 @@ int ai_TryKnockdownFeat(object oCreature, object oTarget) ai_UseFeat(oCreature, FEAT_KNOCKDOWN, oTarget); return TRUE; } +int ai_GetHasPolymorphSelfFeat(object oCreature) +{ + // Lets check to see if we should actually Polymorph? + if(GetHasFeat(FEAT_EPIC_OUTSIDER_SHAPE)) return 3; + else if(GetHasFeat(FEAT_EPIC_CONSTRUCT_SHAPE)) return 3; + else if(GetHasFeat(FEAT_EPIC_WILD_SHAPE_DRAGON)) return 3; + else if(GetHasFeat(FEAT_EPIC_WILD_SHAPE_UNDEAD)) return 3; + else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_4)) return 2; + else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_3)) return 2; + else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_2)) return 1; + else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_1)) return 1; + if(GetHasFeat(FEAT_HUMANOID_SHAPE)) return 0; + else if(GetHasFeat(FEAT_ELEMENTAL_SHAPE)) return 0; + else if(GetHasFeat(FEAT_WILD_SHAPE)) return 0; + return -1; +} int ai_TryPolymorphSelfFeat(object oCreature) { + // Lets check to see if we should actually Polymorph? if(GetHasFeat(FEAT_EPIC_OUTSIDER_SHAPE)) { int nSubFeat = Random(3) + 733; // 733 azer, 734 rakshasa, 735 Slaad. - if(ai_UseFeat(oCreature, FEAT_EPIC_OUTSIDER_SHAPE, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "863", " Using outsider shape 3 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_EPIC_OUTSIDER_SHAPE, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_EPIC_CONSTRUCT_SHAPE)) { int nSubFeat = Random(3) + 738; // 738 Stone, 739 Flesh, 740 Iron. - if(ai_UseFeat(oCreature, FEAT_EPIC_CONSTRUCT_SHAPE, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "868", " Using construct shape 3 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_EPIC_CONSTRUCT_SHAPE, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_EPIC_WILD_SHAPE_DRAGON)) { int nSubFeat = Random(3) + 707; // 707 Red, 708 Blue, 709 Green. - if(ai_UseFeat(oCreature, FEAT_EPIC_WILD_SHAPE_DRAGON, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "873", " Using dragon shape 3 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_EPIC_WILD_SHAPE_DRAGON, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_EPIC_WILD_SHAPE_UNDEAD)) { int nSubFeat = Random(3) + 704; // 704 Risen Lord, 705 Vampire, 706 Spectre. - if(ai_UseFeat(oCreature, FEAT_EPIC_WILD_SHAPE_UNDEAD, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "878", " Using undead shape 3 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_EPIC_WILD_SHAPE_UNDEAD, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_4)) { @@ -881,7 +995,9 @@ int ai_TryPolymorphSelfFeat(object oCreature) if(nRoll == 1) nSubFeat = 679; // Medusa else if(nRoll == 2) nSubFeat = 691; // Mindflayer else nSubFeat = 694; // DireTiger - if(ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_4, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "887", " Using wild shape 3 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_4, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_3)) { @@ -890,7 +1006,9 @@ int ai_TryPolymorphSelfFeat(object oCreature) if(nRoll == 1) nSubFeat = 670; // Basilisk else if(nRoll == 2) nSubFeat = 673; // Drider else nSubFeat = 674; // Manticore - if(ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_3, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "897", " Using wild shape 3 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_3, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_2)) { @@ -899,29 +1017,40 @@ int ai_TryPolymorphSelfFeat(object oCreature) if(nRoll == 1) nSubFeat = 672; // Harpy else if(nRoll == 2) nSubFeat = 678; // Gargoyle else nSubFeat = 680; // Minotaur - if(ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_2, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "910", " Using greater wild shape 2 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_2, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_1)) { int nSubFeat = Random(5) + 658; // Wyrmling - if(ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_1, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "910", " Using greater wild shape 1 feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_GREATER_WILDSHAPE_1, oCreature, nSubFeat); + return TRUE; } if(GetHasFeat(FEAT_HUMANOID_SHAPE)) { int nSubFeat = Random(3) + 682; // 682 Drow, 683 Lizard, 684 Kobold. - if(ai_UseFeat(oCreature, FEAT_HUMANOID_SHAPE, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "915", " Using humanoid shape feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_HUMANOID_SHAPE, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_ELEMENTAL_SHAPE)) { int nSubFeat = Random(4) + SUBFEAT_ELEMENTAL_SHAPE_EARTH; - if(ai_UseFeat(oCreature, FEAT_ELEMENTAL_SHAPE, oCreature, nSubFeat)) return TRUE; + if(AI_DEBUG) ai_Debug("0i_talents", "958", " Using Elemental shape feat: " + IntToString(nSubFeat)); + ai_UseFeat(oCreature, FEAT_ELEMENTAL_SHAPE, oCreature, nSubFeat); + return TRUE; } else if(GetHasFeat(FEAT_WILD_SHAPE)) { int nSubFeat; int nCompanionType = GetAnimalCompanionCreatureType(oCreature); - if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_NONE) + if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_NONE || + !GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS)) + { nSubFeat = Random(5) + SUBFEAT_WILD_SHAPE_BROWN_BEAR; + } else { if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_BADGER) @@ -936,7 +1065,7 @@ int ai_TryPolymorphSelfFeat(object oCreature) nSubFeat = SUBFEAT_WILD_SHAPE_WOLF; else nSubFeat = Random(5) + SUBFEAT_WILD_SHAPE_BROWN_BEAR; } - if(AI_DEBUG) ai_Debug("0i_talents", "885", " Using wild shape feat: " + IntToString(nSubFeat)); + if(AI_DEBUG) ai_Debug("0i_talents", "985", " Using wild shape feat: " + IntToString(nSubFeat)); ai_UseFeat(oCreature, FEAT_WILD_SHAPE, oCreature, nSubFeat); return TRUE; } @@ -1061,6 +1190,7 @@ int ai_TryLayOnHands(object oCreature) int ai_TryTurningTalent(object oCreature) { if(!GetHasFeat(FEAT_TURN_UNDEAD, oCreature)) return FALSE; + if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) return FALSE; if(AI_DEBUG) ai_Debug("0i_talents", "1043", "Checking for Turning Targets."); int nHDCount, nHDCount2, nRacial, nHD; // Get characters levels. @@ -1215,6 +1345,165 @@ int ai_TryWholenessOfBodyFeat(object oCreature) ai_UseFeat(oCreature, FEAT_WHOLENESS_OF_BODY, oCreature); return TRUE; } +//:: [PRC8] Tests & runs Awesome Blow +int ai_TryAwesomeBlowFeat(object oCreature, object oTarget) +{ + //:: FEAT_AWESOME_BLOW (5370) + if(!GetHasFeat(5370, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1250", "ai_TryAwesomeBlowFeat: Sanity Check PASSED!"); + //:: Can't Awesome Blow incorporeal creatures + if(GetLocalInt(oTarget, "X2_L_IS_INCORPOREAL") || ai_GetHasEffectType(oTarget, EFFECT_TYPE_ETHEREAL)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1255", "ai_TryAwesomeBlowFeat: Incorporealty check PASSED!"); + //:: Get sizes + int nSizeCreature = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: Must be larger than target + if(nSizeCreature <= nSizeTarget) + return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1265", "ai_TryAwesomeBlowFeat: Size check PASSED!"); + //:: Should we use Awesome Blow? + if(!ai_CanIAwesomeBlow(oCreature, oTarget)) return FALSE; + ai_UseFeat(oCreature, 5370, oTarget); + return TRUE; +} +//:: [PRC8] Tests & runs Overrun +int ai_TryOverrunFeat(object oCreature, object oTarget) +{ + //:: FEAT_OVERRUN (2811) + if(!GetHasFeat(2811, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1281", "ai_TryOverrunFeat: Sanity Check PASSED!"); + //:: Get sizes + int nSizeCreature = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: One size smaller than target at best. + if(nSizeCreature + 1 <= nSizeTarget) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1291", "ai_TryOverrunFeat: Size Check PASSED!"); + // Should we use Overrun? + if(!ai_CanIOverrun(oCreature, oTarget)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1296", "ai_TryOverrunFeat: ai_CanIOverrun Check PASSED!"); + ai_UseFeat(oCreature, 2811, oTarget); + return TRUE; +} +//:: [PRC8] Tests & runs Bullrush +int ai_TryBullrushFeat(object oCreature, object oTarget) +{ + //:: FEAT_BULLRUSH (2810) + //:: FEAT_IMPROVED_BULLRUSH (2806) + //:: FEAT_BULLRUSH_CHARGE (3413) + if(!GetHasFeat(2810 /* FEAT_BULLRUSH */, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1312", "ai_TryBullrushFeat: Sanity Check PASSED!"); + //:: Get sizes + int nSizeCreature = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: One size smaller than target at best + if(nSizeCreature + 1 <= nSizeTarget) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1322", "ai_TryBullrushFeat: Size Check PASSED!"); + if(d10() > 5) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1327", "ai_TryBullrushFeat: Randomizer PASSED!"); + //:: Verify if attempt is reasonable + if(!ai_CanIBullrush(oCreature, oTarget)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1333", "ai_TryBullrushFeat: ai_CanIBullrush Check PASSED!"); + //:: Decide whether to charge or not + float fDistance = GetDistanceBetween(oCreature, oTarget); + if (fDistance > 3.0 && GetHasFeat(3413 /* FEAT_BULLRUSH_CHARGE */, oCreature)) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1335", "ai_TryBullrushFeat: Using Bullrush Charge."); + ai_UseFeat(oCreature, 3413 /* FEAT_BULLRUSH_CHARGE */, oTarget); + } + else + { + if(AI_DEBUG) ai_Debug("0i_talents", "1340", "ai_TryBullrushFeat: Using Bullrush."); + ai_UseFeat(oCreature, 2810 /* FEAT_BULLRUSH */, oTarget); //:: + } + return TRUE; +} +//:: [PRC8] Tests & runs Ranged Disarm +int ai_TryRangedDisarmFeat(object oCreature, object oTarget) +{ + //:: Sanity checks + if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE; + if(!GetHasFeat(5192 /*FEAT_RANGED_DISARM*/, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1339", "ai_TryRangedDisarmFeat: Sanity Check PASSED!"); + //:: Must have a ranged weapon equipped + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + if(!GetIsObjectValid(oWeapon) || !GetWeaponRanged(oWeapon)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1345", "ai_TryRangedDisarmFeat: Equipped w/ Bow Check PASSED!"); + //:: Target must be disarmable + if(!GetLocalInt(GetModule(), "PRC_PNP_DISARM") && !GetIsCreatureDisarmable(oTarget)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1350", "ai_TryRangedDisarmFeat: Target Disarmable Check PASSED!"); + //:: Target must be holding a weapon + object oTargetWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!GetIsObjectValid(oTargetWeapon)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1350", "ai_TryRangedDisarmFeat: Target Armed Check PASSED!"); + //:: Check range to target - max 30 feet for this feat + if(GetDistanceBetween(oCreature, oTarget) > 30.0) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1350", "ai_TryRangedDisarmFeat: Target Distance Check PASSED!"); + int nOWeaponType = GetBaseItemType(oWeapon); + int nOWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nOWeaponType)); + int nEWeaponType = GetBaseItemType(oTargetWeapon); + int nEWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nEWeaponType)); + int nSizeBonus = (nOWeaponSize - nEWeaponSize) * 4; + //:: Attack penalty check + if (!ai_AttackPenaltyOk(oCreature, oTarget, IntToFloat(nSizeBonus))) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1350", "ai_TryRangedDisarmFeat: Attack Check PASSED!"); + //:: Execute the feat use + ai_UseFeat(oCreature, 5192 /*FEAT_RANGED_DISARM*/, oTarget); + return TRUE; +} +//:: [PRC8] Tests & runs Swarm of Arrows +int ai_TrySwarmofArrowsFeat(object oCreature) +{ + //:: Sanity checks + if(!GetIsObjectValid(oCreature)) return FALSE; + if(!GetHasFeat(25995 /*FEAT_EPIC_SWARM_OF_ARROWS*/, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1369", "ai_TrySwarmofArrowsFeat: Sanity Check PASSED!"); + //:: Must have a longbow or shortbow equipped + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + int nBaseItem = GetBaseItemType(oWeapon); + if(!GetIsObjectValid(oWeapon)) return FALSE; + if(nBaseItem != BASE_ITEM_LONGBOW && nBaseItem != BASE_ITEM_SHORTBOW) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1377", "ai_TrySwarmofArrowsFeat: Weapon Check PASSED!"); + //:: Are we in melee combat? + //int nInMelee = ai_GetNumOfEnemiesInRange(oCreature); + //if( !ai_CanIUseRangedWeapon(oCreature, nInMelee)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1382", "ai_TrySwarmofArrowsFeat: Safe Distance DISABLED!"); + //:: Are there 3+ enemies to attack within 30'? + int nNumOfEnemies = ai_GetNumOfEnemiesInGroup(oCreature, FeetToMeters(30.0)); + if(nNumOfEnemies < 3) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1387", "ai_TrySwarmofArrowsFeat: "+IntToString(nNumOfEnemies)+" enemies in range, PASSED!"); + //:: Execute the feat use + ai_UseFeat(oCreature, 25995 /*FEAT_EPIC_SWARM_OF_ARROWS*/, oCreature); + return TRUE; +} +//:: [PRC8] Tests & runs Inlindl School Strike +int ai_TryInlindlSchoolFeat(object oCreature, object oTarget) +{ + if (!GetHasFeat(5284/* FEAT_INLINDL_SCHOOL */, oCreature)) return FALSE; + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + int nWeaponType = GetBaseItemType(oWeapon); + int nWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nWeaponType)); + int nCreatureSize = GetCreatureSize(oCreature); + if(!GetIsObjectValid(oWeapon)) return FALSE; + if(nWeaponType != BASE_ITEM_RAPIER && nWeaponType != 203 /* BASE_ITEM_ELVEN_THINBLADE */ && + nWeaponType != 204 /* BASE_ITEM_ELVEN_COURTBLADE */ && + (nWeaponSize > 3 || nWeaponSize >= nCreatureSize)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1404", "ai_TryInlindlSchoolFeat: Weapon Check PASSED!"); + //:: Are we in melee combat? + if(ai_GetNumOfEnemiesInRange(oCreature) < 1) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1408", "ai_TryInlindlSchoolFeat: Melee Combat DETECTED!"); + int nShieldAC = ai_GetTotalShieldACBonus(oCreature); + if(nShieldAC < 2) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1412", "Inlindl School: Shield AC Check PASSED!"); + float fACPenalty = -1 * IntToFloat(nShieldAC); + if(AI_DEBUG) ai_Debug("0i_talents", "1416", "ai_TryInlindlSchoolFeat >> Att Bonus: " + IntToString(nShieldAC/2) + " AC Malus: "+FloatToString(fACPenalty)+"."); + if(ai_AttackBonusGood(oCreature, oTarget, IntToFloat(nShieldAC/2))) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1420", "ai_TryInlindlSchoolFeat: Att Bonus PASSED!"); + if(ai_ACAdjustmentGood(oCreature, oTarget, fACPenalty)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1424", "ai_TryInlindlSchoolFeat: AC Malus PASSED!"); + //:: Execute the feat use + ai_UseFeat(oCreature, 5284 /*FEAT_INLINDL_SCHOOL*/, oTarget); + return TRUE; +} // ***************************************************************************** // ******************** Try Physical Attack Talents **************************** // ***************************************************************************** @@ -1259,7 +1548,7 @@ int ai_TryDragonBreathAttack(object oCreature, int nRound, object oTarget = OBJE if(!GetIsTalentValid(tUse)) return FALSE; if(oTarget == OBJECT_INVALID) { - string sIndex = IntToString(ai_GetHighestMeleeIndexNotInAOE(oCreature)); + string sIndex = IntToString(ai_GetHighestGroupIndexNotInAOE(oCreature)); oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); if(oTarget == OBJECT_INVALID) return FALSE; } @@ -1535,7 +1824,7 @@ int ai_TrySneakAttack(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) sIndex = IntToString(ai_GetBestSneakAttackIndex(oCreature, AI_RANGE_MELEE)); } // Ok we are in a serious fight so lets not give attack of opportunities. - else sIndex = IntToString(ai_GetNearestIndex(oCreature, AI_RANGE_MELEE)); + else sIndex = IntToString(ai_GetNearestPhysicalIndex(oCreature, AI_RANGE_MELEE)); oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); } if(oTarget == OBJECT_INVALID) return FALSE; @@ -1564,6 +1853,12 @@ int ai_TryRangedSneakAttack(object oCreature, int nInMelee) ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return TRUE; } +int ai_TryRangedTalents(object oCreature, object oTarget, int nInMelee) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1593", "Check category melee talents!"); + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return TRUE; + return FALSE; +} int ai_TryMeleeTalents(object oCreature, object oTarget) { if(AI_DEBUG) ai_Debug("0i_talents", "1224", "Check category melee talents!"); @@ -1584,8 +1879,117 @@ int ai_TryMeleeTalents(object oCreature, object oTarget) else if(nId == FEAT_DISARM) { if(ai_TryDisarmFeat(oCreature, oTarget)) return TRUE; } else if(nId == FEAT_KI_DAMAGE) { if(ai_TryKiDamageFeat(oCreature, oTarget)) return TRUE; } else if(nId == FEAT_CALLED_SHOT) { if(ai_TryCalledShotFeat(oCreature, oTarget)) return TRUE; } + else if(GetLocalInt(GetModule(), AI_USING_PRC)) + { + if(nId == 2822) { if(ai_TryTripping(oCreature, oTarget)) return TRUE; } //:: FEAT_CM_TRIP (2822) + else if(nId == 2823) { if(ai_TryCharging(oCreature, oTarget)) return TRUE; } //:: FEAT_CM_CHARGE (2823) + else if(nId == 5370) { if(ai_TryAwesomeBlowFeat(oCreature, oTarget)) return TRUE; } //:: FEAT_AWESOME_BLOW (5370) + else if(nId == 2810) { if(ai_TryBullrushFeat(oCreature, oTarget)) return TRUE; } //:: FEAT_BULLRUSH (2810) + else if(nId == 3414) { if(ai_TryGrappling(oCreature, oTarget)) return TRUE; } //:: FEAT_CM_GRAPPLE (3414) + else if(nId == 2811) { if(ai_TryOverrunFeat(oCreature, oTarget)) return TRUE; } //:: FEAT_OVERRUN (2811) + else if(nId == 5284) { if(ai_TryInlindlSchoolFeat(oCreature, oTarget)) return TRUE; } //:: FEAT_INLINDL_SCHOOL (5284) + } return FALSE; } +//:: [PRC8] Tests & runs Trip +int ai_TryTripping(object oCreature, object oTarget) +{ + //:: FEAT_TRIP (2822) + //:: FEAT_IMPROVED_TRIP (2807) + //:: Sanity checks + if(!GetIsObjectValid(oCreature) || !GetIsObjectValid(oTarget)) return FALSE; + if(!GetHasFeat(2822, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1862", "ai_TryTripping: Sanity Checks PASSED!"); + /* int nCoolDown = GetLocalInt(oCreature, "AI_TRIP_COOLDOWN"); + if(nCoolDown > 0) + { + SetLocalInt(oCreature, "AI_TRIP_COOLDOWN", --nCoolDown); + return FALSE; + } */ + int nMySize = GetCreatureSize(oCreature); + int nTheirSize = GetCreatureSize(oTarget); + //:: Prevent silly use of knockdown on immune or too-large targets. + if(GetIsImmune(oTarget, IMMUNITY_TYPE_KNOCKDOWN) || nTheirSize > nMySize + 1) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1879", "ai_TryTripping: Immunity Checks PASSED!"); + if(!ai_CanITrip(oCreature, oTarget)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1883", "ai_TryTripping: ai_CanITrip Check PASSED!"); + ai_UseFeat(oCreature, 2822 /* FEAT_TRIP */, oTarget); + //SetLocalInt(oCreature, "AI_TRIP_COOLDOWN", AI_TRIP_COOLDOWN); + return TRUE; +} +//:: [PRC8] Tests & runs Charge +int ai_TryCharging(object oCreature, object oTarget) +{ + //:: FEAT_CM_CHARGE (2823) + //:: FEAT_BULLRUSH_CHARGE (3413) + //:: FEAT_MINOTAUR_CHARGE (4668) + //:: FEAT_ACROBATIC_CHARGE (2808) + //:: FEAT_SHIELD_CHARGE (3256) + //:: FEAT_POWERFUL_CHARGE (3506) + //:: FEAT_GREATER_POWERFUL_CHARGE (3507) + //:: FEAT_RHINO_TRIBE_CHARGE (3508) + //:: FEAT_FURIOUS_CHARGE (3509) + //:: FEAT_RECKLESS_CHARGE (3510) + //:: FEAT_COBALT_CHARGE (8878) + if (!GetHasFeat(2823, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1901", "ai_TryCharging: Sanity Check PASSED!"); + int nChargeScore = GetHasFeat(2823, oCreature) + // FEAT_CM_CHARGE + GetHasFeat(3413, oCreature) + // FEAT_BULLRUSH_CHARGE + GetHasFeat(4668, oCreature) + // Minotaur + GetHasFeat(2808, oCreature) + // Acrobatic + GetHasFeat(3256, oCreature) + // Shield + GetHasFeat(3506, oCreature) + // Powerful + GetHasFeat(3507, oCreature) + // Greater Powerful + GetHasFeat(3508, oCreature) + // Rhino Tribe + GetHasFeat(3509, oCreature) + // Furious + GetHasFeat(3510, oCreature) + // Reckless + GetHasFeat(8878, oCreature); // Cobalt + if(AI_DEBUG) ai_Debug("0i_talents", "1915", "ai_TryCharging: Charge Score is: "+IntToString(nChargeScore)+"."); + //:: Charge inclination threshold check + if(d20() > 7 + nChargeScore) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1921", "ai_TryCharging: Threshold Check PASSED!"); + //:: Movement check + float f2DASpeed = StringToFloat(Get2DAString("racialtypes", "Endurance", GetRacialType(oCreature))); + if(AI_DEBUG) ai_Debug("0i_talents", "1925", "ai_TryCharging >> 2DA speed is: "+FloatToString(f2DASpeed)+"."); + float fCreatureSpeed = IntToFloat(10 * GetMovementRate(oCreature)); + if(AI_DEBUG) ai_Debug("0i_talents", "1929", "ai_TryCharging >> Creature speed is: "+FloatToString(fCreatureSpeed)+"."); + float fChargeSpeed = (f2DASpeed > fCreatureSpeed) ? f2DASpeed : fCreatureSpeed; + if(AI_DEBUG) ai_Debug("0i_talents", "1929", "ai_TryCharging >> Charge speed is: "+FloatToString(fChargeSpeed)+"."); + //:: If target is too far away to reach in one round, return + if (GetDistanceBetween(oCreature, oTarget) > 2.0 * fChargeSpeed) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1935", "ai_TryCharging: Distance to Target (far) Check PASSED!"); + //:: If target is too close, return + if (GetDistanceBetween(oCreature, oTarget) < FeetToMeters(11.0)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1941", "ai_TryCharging: Distance to Target (near) Check PASSED!"); + //:: Check AB and AC gain/loss + if (!ai_AttackPenaltyOk(oCreature, oTarget, 2.0)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1945", "ai_TryCharging: Attack Mod Check PASSED!"); + if (!ai_ACAdjustmentGood(oCreature, oTarget, -2.0)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1948", "ai_TryCharging: AC Penalty Check PASSED!"); + //:: Use Charge + ai_UseFeat(oCreature, 2823 /* FEAT_CM_CHARGE */, oTarget); + return TRUE; +} +//:: [PRC8] Tests & runs Grapple +int ai_TryGrappling(object oCreature, object oTarget) +{ + //:: FEAT_CM_GRAPPLE (3414) + //:: FEAT_IMPROVED_GRAPPLE (2804) + //:: FEAT_DEVASTATING_GRAPPLE (4246) + if(!GetHasFeat(3414, oCreature)) return FALSE; + //:: Can't grapple incorporeal or ethereal creatures + if(GetLocalInt(oTarget, "X2_L_IS_INCORPOREAL") || ai_GetHasEffectType(oTarget, EFFECT_TYPE_ETHEREAL)) return FALSE; + //:: Get sizes + int nSizeCreature = GetCreatureSize(oCreature); + int nSizeTarget = GetCreatureSize(oTarget); + //:: Must not be grappling up 2+ size categories + if (nSizeTarget - nSizeCreature >= 2) return FALSE; + //:: Should we attempt a grapple? + if (!ai_CanIGrapple(oCreature, oTarget)) return FALSE; + //:: Use the grapple feat + ai_UseFeat(oCreature, 3414, oTarget); + return TRUE; +} // ***************************************************************************** // ***************************** TALENT SCRIPTS ****************************** // ***************************************************************************** @@ -1593,12 +1997,11 @@ int ai_TryMeleeTalents(object oCreature, object oTarget) int ai_GetMonsterTalentMaxLevel(object oCreature) { - // Monsters should use either the best spell they have or a random spell so - // they all don't look robotic. Mix it up based on an Intelligence check. - int nMaxLevel = (ai_GetCharacterLevels(oCreature) + 1) / 2; - if(nMaxLevel > 9) nMaxLevel = 9; - if(AI_DEBUG) ai_Debug("0i_talents", "1258", "nMaxLevel: " + IntToString(nMaxLevel)); - return nMaxLevel; + return 9; + //int nMaxLevel = (ai_GetCharacterLevels(oCreature) + 1) / 2; + //if(nMaxLevel > 9) nMaxLevel = 9; + //if(AI_DEBUG) ai_Debug("0i_talents", "1258", "nMaxLevel: " + IntToString(nMaxLevel)); + //return nMaxLevel; } int ai_GetAssociateTalentMaxLevel(object oCreature, int nDifficulty) { @@ -1642,10 +2045,8 @@ int ai_GetHasTalent(object oCreature, int nTalent) object ai_CheckTalentForBuffing(object oCreature, string sCategory, int nSpell) { // Should we buff this monster caster? Added legacy code just in case. - if((sCategory == "P" || sCategory == "E" || sCategory == "S") && - (GetLocalInt(GetModule(), AI_RULE_BUFF_MONSTERS) || - GetLocalInt(oCreature, "NW_GENERIC_MASTER") & 0x04000000)) return ai_GetBuffTarget(oCreature, nSpell); - //if(sCategory == "S" && GetLocalInt(GetModule(), AI_RULE_PRESUMMON)) return oCreature; + if(sCategory == "P" || sCategory == "E" || + (sCategory == "S" && GetLocalInt(GetModule(), AI_RULE_PRESUMMON))) return ai_GetBuffTarget(oCreature, nSpell); return OBJECT_INVALID; } int ai_UseBuffTalent(object oCreature, int nClass, int nLevel, int nSlot, int nSpell, int nType, object oTarget, object oItem) @@ -1725,7 +2126,7 @@ int ai_SpellRestricted(int nSpell) } return FALSE; } -void ai_SaveTalent(object oCreature, int nClass, int nJsonLevel, int nLevel, int nSlot, int nSpell, int nType, int bMonster, object oItem = OBJECT_INVALID) +void ai_SaveTalent(object oCreature, int nClass, int nJsonLevel, int nLevel, int nSlot, int nSpell, int nType, int bMonster, int bPreBuff, int bFullBuff, object oItem = OBJECT_INVALID) { // Players/Admins can restrict some spells. if(ai_SpellRestricted(nSpell)) return; @@ -1734,15 +2135,17 @@ void ai_SaveTalent(object oCreature, int nClass, int nJsonLevel, int nLevel, int // If it is a blank talent or it is an Area of Effect talent we skip. if(sCategory == "" || sCategory == "A") return; // Check to see if we should be prebuffing. - if(bMonster) + if(bMonster && bPreBuff) { int nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); - if(nSpellBuffDuration == 3) + if(nSpellBuffDuration == 3 || (nSpellBuffDuration == 2 && bFullBuff)) { - if(AI_DEBUG) ai_Debug("0i_talents", "1600", GetName(oCreature) + " is buffing with spell " + IntToString(nSpell)); object oTarget = ai_CheckTalentForBuffing(oCreature, sCategory, nSpell); - if(oTarget != OBJECT_INVALID && - ai_UseBuffTalent(oCreature, nClass, nLevel, nSlot, nSpell, nType, oTarget, oItem)) return; + if(oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1794", GetName(oCreature) + " is prebuffing with spell " + IntToString(nSpell)); + if(ai_UseBuffTalent(oCreature, nClass, nLevel, nSlot, nSpell, nType, oTarget, oItem)) return; + } } } json jCategory = GetLocalJson(oCreature, sCategory); @@ -1780,35 +2183,110 @@ void ai_SaveTalent(object oCreature, int nClass, int nJsonLevel, int nLevel, int jLevel = JsonArrayInsert(jLevel, jTalent); jCategory = JsonArraySet(jCategory, nJsonLevel, jLevel); SetLocalJson(oCreature, sCategory, jCategory); - if(AI_DEBUG) ai_Debug("0i_talents", "1777", sCategory + ": " + JsonDump(jCategory, 1)); - if(AI_DEBUG) ai_Debug("0i_talents", "1778", "AI_MAX_TALENT: " + - IntToString(GetLocalInt(oCreature, AI_MAX_TALENT + sCategory)) + - " nJsonLevel: " + IntToString(nJsonLevel)); + if(AI_DEBUG) ai_Debug("0i_talents", "1835", sCategory + ": " + JsonDump(jCategory, 1)); // Set AI_MAX_TALENT if this talent is higher than the maximum. if(nJsonLevel > GetLocalInt(oCreature, AI_MAX_TALENT + sCategory)) { SetLocalInt(oCreature, AI_MAX_TALENT + sCategory, nJsonLevel); } + if(AI_DEBUG) ai_Debug("0i_talents", "1841", "AI_MAX_TALENT: " + + IntToString(GetLocalInt(oCreature, AI_MAX_TALENT + sCategory)) + + " nJsonLevel: " + IntToString(nJsonLevel)); } // For removing used up spell slots. void ai_RemoveTalent(object oCreature, json jCategory, json jLevel, string sCategory, int nLevel, int nSlotIndex) { - if(AI_DEBUG) ai_Debug("0i_talents", "1400", "removing Talent from slot: " + IntToString(nSlotIndex)); + if(AI_DEBUG) ai_Debug("0i_talents", "1848", "removing Talent from slot: " + IntToString(nSlotIndex)); jLevel = JsonArrayDel(jLevel, nSlotIndex); - if(AI_DEBUG) ai_Debug("0i_talents", "1402", "jLevel: " + JsonDump(jLevel, 2)); + if(AI_DEBUG) ai_Debug("0i_talents", "1850", "jLevel: " + JsonDump(jLevel, 2)); jCategory = JsonArraySet(jCategory, nLevel, jLevel); - if(AI_DEBUG) ai_Debug("0i_talents", "1404", "jCategory: " + JsonDump(jCategory, 2)); + if(AI_DEBUG) ai_Debug("0i_talents", "1852", "jCategory: " + JsonDump(jCategory, 2)); SetLocalJson(oCreature, sCategory, jCategory); } // For removing Sorcerer/Bard spell levels once used up. void ai_RemoveTalentLevel(object oCreature, json jCategory, json jLevel, string sCategory, int nLevel) { - if(AI_DEBUG) ai_Debug("0i_talents", "1410", "removing Talent level: " + IntToString(nLevel)); + if(AI_DEBUG) ai_Debug("0i_talents", "1858", "removing Talent level: " + IntToString(nLevel)); jCategory = JsonArrayDel(jCategory, nLevel); - if(AI_DEBUG) ai_Debug("0i_talents", "1412", "jCategory: " + JsonDump(jCategory, 2)); + jCategory = JsonArraySet(jCategory, nLevel, JsonArray()); + if(AI_DEBUG) ai_Debug("0i_talents", "1861", "jCategory: " + JsonDump(jCategory, 2)); SetLocalJson(oCreature, sCategory, jCategory); } -void ai_SetCreatureSpellTalents(object oCreature, int bMonster) +/******************************************************************************* + New Set Talents by Level then category. +*******************************************************************************/ +/*void ai_SaveTalentByLevel(object oCreature, int nClass, int nJsonLevel, int nLevel, int nSlot, int nSpell, int nType, int bMonster, int bPreBuff, int bFullBuff, object oItem = OBJECT_INVALID) +{ + // Players/Admins can restrict some spells. + if(ai_SpellRestricted(nSpell)) return; + // Get the talent category, we organize all talents by categories. + string sCategory = Get2DAString("ai_spells", "Category", nSpell); + // If it is a blank talent or it is an Area of Effect talent we skip. + if(sCategory == "" || sCategory == "A") return; + // Check to see if we should be prebuffing. + if(bMonster && bPreBuff) + { + int nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); + if(nSpellBuffDuration == 3 || (nSpellBuffDuration == 2 && bFullBuff)) + { + object oTarget = ai_CheckTalentForBuffing(oCreature, sCategory, nSpell); + if(oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1794", GetName(oCreature) + " is prebuffing with spell " + IntToString(nSpell)); + if(ai_UseBuffTalent(oCreature, nClass, nLevel, nSlot, nSpell, nType, oTarget, oItem)) return; + } + } + } + string sLevel = IntToString(nLevel); + // Get the Level saved to Json. + json jLevel = GetLocalJson(oCreature, "SPELL_LEVEL_" + sLevel); + // If jLevel is not created we set it up. + if(JsonGetType(jLevel) == JSON_TYPE_NULL) jLevel = JsonArray(); + json jTalent = JsonArray(); + if(nType == AI_TALENT_TYPE_SPELL || nType == AI_TALENT_TYPE_SP_ABILITY) + { + jTalent = JsonArrayInsert(jTalent, JsonInt(nType), 0); + jTalent = JsonArrayInsert(jTalent, JsonInt(nSpell)); + jTalent = JsonArrayInsert(jTalent, JsonInt(nClass)); + jTalent = JsonArrayInsert(jTalent, JsonInt(nLevel)); + jTalent = JsonArrayInsert(jTalent, JsonInt(nSlot)); + jTalent = JsonArrayInsert(jTalent, JsonString(sCategory)); + } + else if(nType == AI_TALENT_TYPE_ITEM) + { + jTalent = JsonArrayInsert(jTalent, JsonInt(nType), 0); + jTalent = JsonArrayInsert(jTalent, JsonInt(nSpell)); + jTalent = JsonArrayInsert(jTalent, JsonString(ObjectToString(oItem))); + jTalent = JsonArrayInsert(jTalent, JsonInt(nLevel)); + jTalent = JsonArrayInsert(jTalent, JsonInt(nSlot)); + jTalent = JsonArrayInsert(jTalent, JsonString(sCategory)); + } + jLevel = JsonArrayInsert(jLevel, jTalent); + SetLocalJson(oCreature, "SPELL_LEVEL_" + sLevel, jLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "2265", sLevel + ": " + JsonDump(jLevel, 1)); + // Set AI_MAX_TALENT if this talent is higher than the maximum. + if(nJsonLevel > GetLocalInt(oCreature, AI_MAX_LEVEL + sLevel)) + { + SetLocalInt(oCreature, AI_MAX_LEVEL + sLevel, nJsonLevel); + } + if(AI_DEBUG) ai_Debug("0i_talents", "2271", "AI_MAX_LEVEL: " + + IntToString(GetLocalInt(oCreature, AI_MAX_LEVEL + sLevel)) + + " nJsonLevel: " + IntToString(nJsonLevel)); +} +// For removing used up spell slots. +void ai_RemoveTalentByLevel(object oCreature, json jLevel, int nLevel, int nSlotIndex) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "2278", "removing Talent from slot: " + IntToString(nSlotIndex)); + jLevel = JsonArrayDel(jLevel, nSlotIndex); + if(AI_DEBUG) ai_Debug("0i_talents", "2280", "jLevel: " + JsonDump(jLevel, 2)); + SetLocalJson(oCreature, "SPELL_LEVEL_" + IntToString(nLevel), jLevel); +} +// For removing Sorcerer/Bard spell levels once used up. +void ai_RemoveTalentLevelByLevel(object oCreature, int nLevel) +{ + DeleteLocalJson(oCreature, "SPELL_LEVEL_" + IntToString(nLevel)); +} +void ai_SetCreatureSpellTalentsByLevel(object oCreature, int bMonster, int bPreBuff, int bFullBuff) { if(AI_DEBUG) ai_Debug("0i_talents", "1417", GetName(oCreature) + ": Setting Spell Talents for combat [Buff: " + IntToString(bMonster) + "]."); @@ -1860,8 +2338,8 @@ void ai_SetCreatureSpellTalents(object oCreature, int bMonster) nAdjLevel = nLevel + nMetaMagic; if(nAdjLevel > 9) nAdjLevel = 9; } - else nAdjLevel = nLevel; */ - ai_SaveTalent(oCreature, nClass, nLevel, nLevel, nSlot, nSpell, AI_TALENT_TYPE_SPELL, bMonster); + else nAdjLevel = nLevel; + ai_SaveTalentByLevel(oCreature, nClass, nLevel, nLevel, nSlot, nSpell, AI_TALENT_TYPE_SPELL, bMonster, bPreBuff, bFullBuff); } nSlot++; } @@ -1890,7 +2368,7 @@ void ai_SetCreatureSpellTalents(object oCreature, int bMonster) IntToString(GetSpellUsesLeft(oCreature, nClass, nSpell))); if(GetSpellUsesLeft(oCreature, nClass, nSpell) > 0) { - ai_SaveTalent(oCreature, nClass, nLevel, nLevel, nSlot, nSpell, AI_TALENT_TYPE_SPELL, bMonster); + ai_SaveTalentByLevel(oCreature, nClass, nLevel, nLevel, nSlot, nSpell, AI_TALENT_TYPE_SPELL, bMonster, bPreBuff, bFullBuff); } nSlot++; } @@ -1902,12 +2380,12 @@ void ai_SetCreatureSpellTalents(object oCreature, int bMonster) nClass = GetClassByPosition(nClassPosition, oCreature); } } -void ai_SetCreatureSpecialAbilityTalents(object oCreature, int bMonster) +void ai_SetCreatureSpecialAbilityTalentsByLevel(object oCreature, int bMonster, int bPreBuff, int bFullBuff) { if(AI_DEBUG) ai_Debug("0i_talents", "1488", GetName(oCreature) + ": Setting Special Ability Talents for combat."); // Cycle through all the creatures special abilities. int nMaxSpecialAbilities = GetSpellAbilityCount(oCreature); - if(AI_DEBUG) ai_Debug("0i_talents", "1491", IntToString(GetSpellAbilityCount(oCreature)) + " Spell abilities."); + if(AI_DEBUG) ai_Debug("0i_talents", "1491", IntToString(nMaxSpecialAbilities) + " Spell abilities."); if(nMaxSpecialAbilities) { int nIndex, nSpell, nLevel; @@ -1917,13 +2395,13 @@ void ai_SetCreatureSpecialAbilityTalents(object oCreature, int bMonster) if(GetSpellAbilityReady(oCreature, nSpell)) { nLevel = StringToInt(Get2DAString("spells", "Innate", nSpell)); - ai_SaveTalent(oCreature, 255, nLevel, nLevel, nIndex, nSpell, AI_TALENT_TYPE_SP_ABILITY, bMonster); + ai_SaveTalentByLevel(oCreature, 255, nLevel, nLevel, nIndex, nSpell, AI_TALENT_TYPE_SP_ABILITY, bMonster, bPreBuff, bFullBuff); } nIndex++; } } } -void ai_CheckItemProperties(object oCreature, object oItem, int bMonster, int bEquiped = FALSE) +void ai_CheckItemPropertiesByLevel(object oCreature, object oItem, int bMonster, int bPreBuff, int bFullBuff, int bEquiped = FALSE) { if(AI_DEBUG) ai_Debug("0i_talents", "1509", "Checking Item properties on " + GetName(oItem)); // We have established that we can use the item if it is equiped. @@ -1969,7 +2447,7 @@ void ai_CheckItemProperties(object oCreature, object oItem, int bMonster, int bE nIprpSubType = GetItemPropertySubType(ipProp); nSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nIprpSubType)); nLevel = StringToInt(Get2DAString("iprp_spells", "InnateLvl", nIprpSubType)); - ai_SaveTalent(oCreature, 255, nLevel, nLevel, nIndex, nSpell, AI_TALENT_TYPE_ITEM, bMonster, oItem); + ai_SaveTalentByLevel(oCreature, 255, nLevel, nLevel, nIndex, nSpell, AI_TALENT_TYPE_ITEM, bMonster, bPreBuff, bFullBuff, oItem); } } else if(nIPType == ITEM_PROPERTY_HEALERS_KIT) @@ -1980,7 +2458,7 @@ void ai_CheckItemProperties(object oCreature, object oItem, int bMonster, int bE // Must also have ranks in healing kits. if(GetSkillRank(SKILL_HEAL, oCreature) > 0) { - ai_SaveTalent(oCreature, 255, 7, 0, nIndex, nSpell, AI_TALENT_TYPE_ITEM, bMonster, oItem); + ai_SaveTalent(oCreature, 255, 7, 0, nIndex, nSpell, AI_TALENT_TYPE_ITEM, bMonster, bPreBuff, bFullBuff, oItem); } } if(bEquiped) @@ -2041,13 +2519,15 @@ void ai_CheckItemProperties(object oCreature, object oItem, int bMonster, int bE nIndex++; ipProp = GetNextItemProperty(oItem); } - // If nSpellImmunity has been set then we need to save our Immunity json. + // If any Immunity has been set then we need to save our Immunity json. if(bHasItemImmunity) SetLocalJson(oCreature, AI_TALENT_IMMUNITY, jImmunity); } -void ai_SetCreatureItemTalents(object oCreature, int bMonster) +void ai_SetCreatureItemTalentsByLevel(object oCreature, int bMonster, int bPreBuff, int bFullBuff) { if(AI_DEBUG) ai_Debug("0i_talents", "1561", GetName(oCreature) + ": Setting Item Talents for combat."); int bEquiped; + // Set the Immunities to -1 so we know they were set incase there are no immunities. + SetLocalInt(oCreature, sIPReducedVarname, -1); string sSlots; // Cycle through all the creatures inventory items. object oItem = GetFirstItemInInventory(oCreature); @@ -2058,7 +2538,7 @@ void ai_SetCreatureItemTalents(object oCreature, int bMonster) // Does the item need to be equiped to use its powers? sSlots = Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)); if(AI_DEBUG) ai_Debug("0i_talents", "1572", GetName(oItem) + " requires " + Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)) + " slots."); - if(sSlots == "0x00000") ai_CheckItemProperties(oCreature, oItem, bMonster); + if(sSlots == "0x00000") ai_CheckItemPropertiesByLevel(oCreature, oItem, bMonster, bPreBuff, bFullBuff); } oItem = GetNextItemInInventory(oCreature); } @@ -2067,28 +2547,331 @@ void ai_SetCreatureItemTalents(object oCreature, int bMonster) oItem = GetItemInSlot(nSlot, oCreature); while(nSlot < 11) { - if(oItem != OBJECT_INVALID) ai_CheckItemProperties(oCreature, oItem, bMonster, TRUE); + if(oItem != OBJECT_INVALID) ai_CheckItemPropertiesByLevel(oCreature, oItem, bMonster, bPreBuff, bFullBuff, TRUE); oItem = GetItemInSlot(++nSlot, oCreature); } oItem = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature); - if(oItem != OBJECT_INVALID) ai_CheckItemProperties(oCreature, oItem, bMonster, TRUE); + if(oItem != OBJECT_INVALID) ai_CheckItemPropertiesByLevel(oCreature, oItem, bMonster, bPreBuff, bFullBuff, TRUE); } -void ai_SetCreatureTalents(object oCreature, int bMonster) +void ai_SetCreatureTalentsByLevel(object oCreature, int bMonster, int bForceTalentSetup = FALSE) { - json jCreature = ObjectToJson(oCreature); + //json jCreature = ObjectToJson(oCreature); //if(AI_DEBUG) ai_Debug("0i_talents", "2072", GetName(oCreature) + " jCreature: " + JsonDump(jCreature, 4)); - if(GetLocalInt(oCreature, AI_TALENTS_SET)) return; + if(GetLocalInt(oCreature, AI_TALENTS_SET) && !bForceTalentSetup) return; SetLocalInt(oCreature, AI_TALENTS_SET, TRUE); object oModule = GetModule(); + int bPreBuff = GetLocalInt(GetModule(), AI_RULE_BUFF_MONSTERS) || (GetLocalInt(oCreature, "NW_GENERIC_MASTER") & 0x04000000); + int bFullBuff = GetLocalInt(GetModule(), AI_RULE_FULL_BUFF_MONSTERS); + if(bForceTalentSetup) bPreBuff = FALSE; ai_Counter_Start(); - ai_SetCreatureSpellTalents(oCreature, bMonster); + ai_SetCreatureSpellTalentsByLevel(oCreature, bMonster, bPreBuff, bFullBuff); ai_Counter_End(GetName(oCreature) + ": Spell Talents"); - ai_SetCreatureSpecialAbilityTalents(oCreature, bMonster); + ai_SetCreatureSpecialAbilityTalentsByLevel(oCreature, bMonster, bPreBuff, bFullBuff); ai_Counter_End(GetName(oCreature) + ": Special Ability Talents"); DeleteLocalJson(oCreature, AI_TALENT_IMMUNITY); - ai_SetCreatureItemTalents(oCreature, bMonster); + ai_SetCreatureItemTalentsByLevel(oCreature, bMonster, bPreBuff, bFullBuff); ai_Counter_End(GetName(oCreature) + ": Item Talents"); - if(GetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS) && GetLocalInt(oModule, AI_RULE_PRESUMMON) && bMonster) + if(GetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS) && + GetLocalInt(oModule, AI_RULE_PRESUMMON) && bMonster && bPreBuff) + { + ai_TrySummonFamiliarTalent(oCreature); + ai_TrySummonAnimalCompanionTalent(oCreature); + } + // AI_CAT_CURE is setup differently we save the level as the highest. + //if(JsonGetType(GetLocalJson(oCreature, AI_TALENT_CURE)) != JSON_TYPE_NULL) SetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE, 9); + // With spontaneous cure spells we need to clear this as the number of spells don't count. + //if(GetLevelByClass(CLASS_TYPE_CLERIC, oCreature)) SetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_HEALING, 0); +} */ +/******************************************************************************* + Old Set talents by category then level. +*******************************************************************************/ +void ai_SetCreatureSpellTalents(object oCreature, int bMonster, int bPreBuff, int bFullBuff) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1417", GetName(oCreature) + ": Setting Spell Talents for combat [Buff: " + + IntToString(bMonster) + "]."); + // Cycle through all classes and spells. + int nClassPosition = 1, nMaxSlot, nLevel, nSlot, nSpell, nIndex, nMetaMagic; + int nClass = GetClassByPosition(nClassPosition, oCreature); + while(nClassPosition <= AI_MAX_CLASSES_PER_CHARACTER && nClass != CLASS_TYPE_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1824", "nClass: " + IntToString(nClass) + + " nClassPosition: " + IntToString(nClassPosition) + + " SpellCaster: " + Get2DAString("classes", "SpellCaster", nClass) + + " Memorized: " + Get2DAString("classes", "MemorizesSpells", nClass)); + if(Get2DAString("classes", "SpellCaster", nClass) == "1") + { + // Search all memorized spells for the spell. + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + // Check each level organizing from highest to lowest. + nLevel = (GetLevelByPosition(nClassPosition, oCreature) + 1) / 2; + if(nLevel > 9) nLevel = 9; + while(nLevel > -1) + { + // Check each slot within each level. + nMaxSlot = GetMemorizedSpellCountByLevel(oCreature, nClass, nLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "1434", "nClass: " + IntToString(nClass) + + " nLevel: " + IntToString(nLevel) + " nMaxSlot: " + + IntToString(nMaxSlot)); + nSlot = 0; + while(nSlot < nMaxSlot) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1440", "nSlot: " + IntToString(nSlot) + " nSpell: " + + IntToString(GetMemorizedSpellId(oCreature, nClass, nLevel, nSlot)) + " spell memorized: " + + IntToString(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot))); + if(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot) == 1) + { + nSpell = GetMemorizedSpellId(oCreature, nClass, nLevel, nSlot); + /* Spells are already at the higher level when saved as a talent. + // Move a spell up to a different JsonLevel as higher Jsonlevel + // spells usually get cast first. + nMetaMagic = GetMemorizedSpellMetaMagic(oCreature, nClass, nLevel, nSlot); + if(nMetaMagic > 0) + { + if(nMetaMagic == METAMAGIC_STILL) nMetaMagic = 1; + else if(nMetaMagic == METAMAGIC_EXTEND) nMetaMagic = 1; + else if(nMetaMagic == METAMAGIC_SILENT) nMetaMagic = 1; + else if(nMetaMagic == METAMAGIC_EMPOWER) nMetaMagic = 2; + else if(nMetaMagic == METAMAGIC_MAXIMIZE) nMetaMagic = 3; + else if(nMetaMagic == METAMAGIC_QUICKEN) nMetaMagic = 4; + nAdjLevel = nLevel + nMetaMagic; + if(nAdjLevel > 9) nAdjLevel = 9; + } + else nAdjLevel = nLevel; */ + ai_SaveTalent(oCreature, nClass, nLevel, nLevel, nSlot, nSpell, AI_TALENT_TYPE_SPELL, bMonster, bPreBuff, bFullBuff); + } + nSlot++; + } + nLevel--; + } + } + // Check non-memorized known lists for the spell. + else + { + // Check each level starting with the highest to lowest. + nLevel = (GetLevelByPosition(nClassPosition, oCreature) + 1) / 2; + if(nLevel > 9) nLevel = 9; + while(nLevel > -1) + { + // Check each slot within each level. + nMaxSlot = GetKnownSpellCount(oCreature, nClass, nLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "1462", "nClass: " + IntToString(nClass) + + " nLevel: " + IntToString(nLevel) + " nMaxSlot: " + + IntToString(nMaxSlot)); + nSlot = 0; + while(nSlot < nMaxSlot) + { + nSpell = GetKnownSpellId(oCreature, nClass, nLevel, nSlot); + if(AI_DEBUG) ai_Debug("0i_talents", "1469", "nSlot: " + IntToString(nSlot) + + " nSpell: " + IntToString(nSpell) + " nUsesLeft: " + + IntToString(GetSpellUsesLeft(oCreature, nClass, nSpell))); + if(GetSpellUsesLeft(oCreature, nClass, nSpell) > 0) + { + ai_SaveTalent(oCreature, nClass, nLevel, nLevel, nSlot, nSpell, AI_TALENT_TYPE_SPELL, bMonster, bPreBuff, bFullBuff); + } + nSlot++; + } + nLevel--; + } + } + } + nClassPosition++; + nClass = GetClassByPosition(nClassPosition, oCreature); + } +} +void ai_SetCreatureSpecialAbilityTalents(object oCreature, int bMonster, int bPreBuff, int bFullBuff) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1488", GetName(oCreature) + ": Setting Special Ability Talents for combat."); + // Cycle through all the creatures special abilities. + int nMaxSpecialAbilities = GetSpellAbilityCount(oCreature); + if(AI_DEBUG) ai_Debug("0i_talents", "1491", IntToString(nMaxSpecialAbilities) + " Spell abilities."); + if(nMaxSpecialAbilities) + { + int nIndex, nSpell, nLevel; + while(nIndex < nMaxSpecialAbilities) + { + nSpell = GetSpellAbilitySpell(oCreature, nIndex); + if(GetSpellAbilityReady(oCreature, nSpell)) + { + nLevel = StringToInt(Get2DAString("spells", "Innate", nSpell)); + ai_SaveTalent(oCreature, 255, nLevel, nLevel, nIndex, nSpell, AI_TALENT_TYPE_SP_ABILITY, bMonster, bPreBuff, bFullBuff); + } + nIndex++; + } + } +} +void ai_CheckItemProperties(object oCreature, object oItem, int bMonster, int bPreBuff, int bFullBuff, int bEquiped = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1509", "Checking Item properties on " + GetName(oItem)); + // We have established that we can use the item if it is equiped. + if(!bEquiped && !ai_CheckIfCanUseItem(oCreature, oItem)) return; + // Get or create an Immunity in json so we can check item immunities quickly. + int nSpellImmunity, bHasItemImmunity, nPerDay, nCharges, nUses, bSaveTalent; + json jImmunity = GetLocalJson(oCreature, AI_TALENT_IMMUNITY); + if(JsonGetType(jImmunity) == JSON_TYPE_NULL) jImmunity = JsonArray(); + int nIprpSubType, nSpell, nLevel, nIPType, nIndex; + itemproperty ipProp = GetFirstItemProperty(oItem); + // Lets skip this if there are no properties. + if(!GetIsItemPropertyValid(ipProp)) return; + // Check for cast spell property and add them to the talent list. + while(GetIsItemPropertyValid(ipProp)) + { + nIPType = GetItemPropertyType(ipProp); + if(AI_DEBUG) ai_Debug("0i_talents", "1895", "ItempropertyType(15/80/53): " + IntToString(nIPType)); + if(nIPType == ITEM_PROPERTY_CAST_SPELL) + { + bSaveTalent = TRUE; + // Get how they use the item (charges or uses per day). + nUses = GetItemPropertyCostTableValue(ipProp); + if(nUses > 1 && nUses < 7) + { + nCharges = GetItemCharges(oItem); + if(AI_DEBUG) ai_Debug("0i_talents", "1530", "Charges per use: " + IntToString(nUses) + + " Item charges: " + IntToString(nCharges)); + if((nUses == IP_CONST_CASTSPELL_NUMUSES_1_CHARGE_PER_USE && nCharges < 1) || + (nUses == IP_CONST_CASTSPELL_NUMUSES_2_CHARGES_PER_USE && nCharges < 2) || + (nUses == IP_CONST_CASTSPELL_NUMUSES_3_CHARGES_PER_USE && nCharges < 3) || + (nUses == IP_CONST_CASTSPELL_NUMUSES_4_CHARGES_PER_USE && nCharges < 4) || + (nUses == IP_CONST_CASTSPELL_NUMUSES_5_CHARGES_PER_USE && nCharges < 5)) bSaveTalent = FALSE; + } + else if(nUses > 7 && nUses < 13) + { + nPerDay = GetItemPropertyUsesPerDayRemaining(oItem, ipProp); + if(AI_DEBUG) ai_Debug("0i_talents", "1676", "Item uses: " + IntToString(nPerDay)); + if(nPerDay == 0) bSaveTalent = FALSE; + } + if(bSaveTalent) + { + // SubType is the ip spell index for iprp_spells.2da + nIprpSubType = GetItemPropertySubType(ipProp); + nSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nIprpSubType)); + nLevel = StringToInt(Get2DAString("iprp_spells", "InnateLvl", nIprpSubType)); + ai_SaveTalent(oCreature, 255, nLevel, nLevel, nIndex, nSpell, AI_TALENT_TYPE_ITEM, bMonster, bPreBuff, bFullBuff, oItem); + } + } + else if(nIPType == ITEM_PROPERTY_HEALERS_KIT) + { + // Lets set Healing kits as Cure Light Wounds since they heal 1d20 in combat. + nSpell = SPELL_CURE_MINOR_WOUNDS; + // Save the healer kit as level 9 so we can use them first. + // Must also have ranks in healing kits. + if(GetSkillRank(SKILL_HEAL, oCreature) > 0) + { + ai_SaveTalent(oCreature, 255, 7, 0, nIndex, nSpell, AI_TALENT_TYPE_ITEM, bMonster, bPreBuff, bFullBuff, oItem); + } + } + if(bEquiped) + { + if(nIPType == ITEM_PROPERTY_IMMUNITY_SPECIFIC_SPELL) + { + bHasItemImmunity = TRUE; + nSpellImmunity = GetItemPropertyCostTableValue(ipProp); + nSpellImmunity = StringToInt(Get2DAString("iprp_spellcost", "SpellIndex", nSpellImmunity)); + //if(AI_DEBUG) ai_Debug("0i_talents", "1950", "SpellImmunity to " + Get2DAString("spells", "Label", nSpellImmunity)); + jImmunity = JsonArrayInsert(jImmunity, JsonInt(nSpellImmunity)); + } + else if(nIPType == ITEM_PROPERTY_HASTE) + { + SetLocalInt(oCreature, sIPHasHasteVarname, TRUE); + } + else if(nIPType == ITEM_PROPERTY_IMMUNITY_DAMAGE_TYPE) + { + int nBit, nIpSubType = GetItemPropertySubType(ipProp); + if(AI_DEBUG) ai_Debug("0i_talents", "1957", "nIPSubType: " + IntToString(nIpSubType)); + if(nIpSubType == 0) nBit = DAMAGE_TYPE_BLUDGEONING; + else if(nIpSubType == 1) nBit = DAMAGE_TYPE_PIERCING; + else if(nIpSubType == 2) nBit = DAMAGE_TYPE_SLASHING; + else if(nIpSubType == 5) nBit = DAMAGE_TYPE_MAGICAL; + else if(nIpSubType == 6) nBit = DAMAGE_TYPE_ACID; + else if(nIpSubType == 7) nBit = DAMAGE_TYPE_COLD; + else if(nIpSubType == 8) nBit = DAMAGE_TYPE_DIVINE; + else if(nIpSubType == 9) nBit = DAMAGE_TYPE_ELECTRICAL; + else if(nIpSubType == 10) nBit = DAMAGE_TYPE_FIRE; + else if(nIpSubType == 11) nBit = DAMAGE_TYPE_NEGATIVE; + else if(nIpSubType == 12) nBit = DAMAGE_TYPE_POSITIVE; + else if(nIpSubType == 13) nBit = DAMAGE_TYPE_SONIC; + if(nBit > 0) ai_SetItemProperty(oCreature, sIPImmuneVarname, nBit, TRUE); + } + else if(nIPType == ITEM_PROPERTY_DAMAGE_RESISTANCE) + { + int nBit, nIpSubType = GetItemPropertySubType(ipProp); + if(nIpSubType == 0) nBit = DAMAGE_TYPE_BLUDGEONING; + else if(nIpSubType == 1) nBit = DAMAGE_TYPE_PIERCING; + else if(nIpSubType == 2) nBit = DAMAGE_TYPE_SLASHING; + else if(nIpSubType == 5) nBit = DAMAGE_TYPE_MAGICAL; + else if(nIpSubType == 6) nBit = DAMAGE_TYPE_ACID; + else if(nIpSubType == 7) nBit = DAMAGE_TYPE_COLD; + else if(nIpSubType == 8) nBit = DAMAGE_TYPE_DIVINE; + else if(nIpSubType == 9) nBit = DAMAGE_TYPE_ELECTRICAL; + else if(nIpSubType == 10) nBit = DAMAGE_TYPE_FIRE; + else if(nIpSubType == 11) nBit = DAMAGE_TYPE_NEGATIVE; + else if(nIpSubType == 12) nBit = DAMAGE_TYPE_POSITIVE; + else if(nIpSubType == 13) nBit = DAMAGE_TYPE_SONIC; + if(nBit > 0) ai_SetItemProperty(oCreature, sIPResistVarname, nBit, TRUE); + } + else if(nIPType == ITEM_PROPERTY_DAMAGE_REDUCTION) + { + int nIpSubType = GetItemPropertySubType(ipProp); + SetLocalInt(oCreature, sIPReducedVarname, nIpSubType); + } + } + nIndex++; + ipProp = GetNextItemProperty(oItem); + } + // If any Immunity has been set then we need to save our Immunity json. + if(bHasItemImmunity) SetLocalJson(oCreature, AI_TALENT_IMMUNITY, jImmunity); +} +void ai_SetCreatureItemTalents(object oCreature, int bMonster, int bPreBuff, int bFullBuff) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1561", GetName(oCreature) + ": Setting Item Talents for combat."); + int bEquiped; + // Set the Immunities to -1 so we know they were set incase there are no immunities. + SetLocalInt(oCreature, sIPReducedVarname, -1); + string sSlots; + // Cycle through all the creatures inventory items. + object oItem = GetFirstItemInInventory(oCreature); + while(oItem != OBJECT_INVALID) + { + if(GetIdentified(oItem)) + { + // Does the item need to be equiped to use its powers? + sSlots = Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)); + if(AI_DEBUG) ai_Debug("0i_talents", "1572", GetName(oItem) + " requires " + Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)) + " slots."); + if(sSlots == "0x00000") ai_CheckItemProperties(oCreature, oItem, bMonster, bPreBuff, bFullBuff); + } + oItem = GetNextItemInInventory(oCreature); + } + int nSlot; + // Cycle through all the creatures equiped items. + oItem = GetItemInSlot(nSlot, oCreature); + while(nSlot < 11) + { + if(oItem != OBJECT_INVALID) ai_CheckItemProperties(oCreature, oItem, bMonster, bPreBuff, bFullBuff, TRUE); + oItem = GetItemInSlot(++nSlot, oCreature); + } + oItem = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature); + if(oItem != OBJECT_INVALID) ai_CheckItemProperties(oCreature, oItem, bMonster, bPreBuff, bFullBuff, TRUE); +} +void ai_SetCreatureTalents(object oCreature, int bMonster, int bForceTalentSetup = FALSE) +{ + //json jCreature = ObjectToJson(oCreature); + //if(AI_DEBUG) ai_Debug("0i_talents", "2072", GetName(oCreature) + " jCreature: " + JsonDump(jCreature, 4)); + if(GetLocalInt(oCreature, AI_TALENTS_SET) && !bForceTalentSetup) return; + SetLocalInt(oCreature, AI_TALENTS_SET, TRUE); + object oModule = GetModule(); + int bPreBuff = GetLocalInt(GetModule(), AI_RULE_BUFF_MONSTERS) || (GetLocalInt(oCreature, "NW_GENERIC_MASTER") & 0x04000000); + int bFullBuff = GetLocalInt(GetModule(), AI_RULE_FULL_BUFF_MONSTERS); + if(bForceTalentSetup) bPreBuff = FALSE; + ai_Counter_Start(); + ai_SetCreatureSpellTalents(oCreature, bMonster, bPreBuff, bFullBuff); + ai_Counter_End(GetName(oCreature) + ": Spell Talents"); + ai_SetCreatureSpecialAbilityTalents(oCreature, bMonster, bPreBuff, bFullBuff); + ai_Counter_End(GetName(oCreature) + ": Special Ability Talents"); + DeleteLocalJson(oCreature, AI_TALENT_IMMUNITY); + ai_SetCreatureItemTalents(oCreature, bMonster, bPreBuff, bFullBuff); + ai_Counter_End(GetName(oCreature) + ": Item Talents"); + if(GetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS) && + GetLocalInt(oModule, AI_RULE_PRESUMMON) && bMonster && bPreBuff) { ai_TrySummonFamiliarTalent(oCreature); ai_TrySummonAnimalCompanionTalent(oCreature); @@ -2214,20 +2997,6 @@ int ai_UseCreatureSpellTalent(object oCreature, json jLevel, json jTalent, strin } } if(ai_ArcaneSpellFailureTooHigh(oCreature, nClass, nLevel, nSlot)) return FALSE; - if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") - { - // Shouldn't need this anymore, we need to do a debug looking at this. - if(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot) < 1) return FALSE; - if(ai_CheckSpecialTalentsandUse(oCreature, jTalent, sCategory, nInMelee, oTarget)) - { - if(ai_CompareLastAction(oCreature, AI_LAST_ACTION_CAST_SPELL)) return -1; - return TRUE; - } - return FALSE; - } - if(AI_DEBUG) ai_Debug("0i_talents", "1629", "Known caster Level: " + IntToString(nLevel) + - " Uses : " + IntToString(GetSpellUsesLeft(oCreature, nClass, JsonGetInt(JsonArrayGet(jTalent, 1))))); - if(!GetSpellUsesLeft(oCreature, nClass, JsonGetInt(JsonArrayGet(jTalent, 1)))) return -2; return ai_CheckSpecialTalentsandUse(oCreature, jTalent, sCategory, nInMelee, oTarget); } int ai_UseCreatureItemTalent(object oCreature, json jLevel, json jTalent, string sCategory, int nInMelee, object oTarget = OBJECT_INVALID) @@ -2268,43 +3037,68 @@ int ai_UseCreatureItemTalent(object oCreature, json jLevel, json jTalent, string if(ai_CheckSpecialTalentsandUse(oCreature, jTalent, sCategory, nInMelee, oTarget)) return TRUE; return FALSE; } -int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int nLevel = 10, object oTarget = OBJECT_INVALID) +void ai_UpdateMaxTalentLevel(object oCreature, json jCategory, string sCategory, int nMaxTalentLevel, int nLevel) { - // Get the saved category from oCreature. - json jCategory = GetLocalJson(oCreature, sCategory); - if(AI_DEBUG) ai_Debug("0i_talents", "2292", "jCategory: " + sCategory + " " + JsonDump(jCategory, 2)); - if(JsonGetType(jCategory) == JSON_TYPE_NULL) return FALSE; - // If there are no talents at lower levels then start at the lower level. - int nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + sCategory); - if(AI_DEBUG) ai_Debug("0i_talents", "2297", AI_MAX_TALENT + sCategory + ": " + + // We only reduce the MaxTalentLevel if this is the top most talent level. + if(nMaxTalentLevel == nLevel) + { + json jLevel = JsonArrayGet(jCategory, --nLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "2331", "jLevel length: " + IntToString(JsonGetLength(jLevel))); + while(JsonGetLength(jLevel) == 0 && nLevel > -1) + { + jLevel = JsonArrayGet(jCategory, --nLevel); + } + if(AI_DEBUG) ai_Debug("0i_talents", "2335", "Setting AI_MAX_TALENT_" + sCategory + " to " + IntToString(nLevel)); + SetLocalInt(oCreature, AI_MAX_TALENT + sCategory, nLevel); + } +} +/******************************************************************************* + New talent system that checks by level then category. +*******************************************************************************/ +/*int ai_UseCreatureTalentByLevel(object oCreature, int nInMelee, int nLevel = 10, object oTarget = OBJECT_INVALID) +{ + string sLevel = IntToString(nLevel); + // Get the Max Talent Level to see if we even need to pull this talent. + int nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_LEVEL + sLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "2394", AI_MAX_LEVEL + sLevel + ": " + IntToString(nMaxTalentLevel) + " nLevel: " + IntToString(nLevel)); + if(nMaxTalentLevel == -1) return FALSE; + // If there are no talents at higher levels then start at the lowest talent level. if(nMaxTalentLevel < nLevel) nLevel = nMaxTalentLevel; if(nLevel < 0 || nLevel > 10) nLevel = 9; - json jLevel, jTalent; - int nClass, nSlot, nType, nSlotIndex, nMaxSlotIndex, nTalentUsed, nSpell; + json jTalent; + int bHasTalent, nClass, nSlot, nType, nSlotIndex, nMaxSlotIndex, nTalentUsed, nSpell; int bUseMagic = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC); int bUseMagicItems = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC_ITEMS); - if(AI_DEBUG) ai_Debug("0i_talents", "2305", "bUseMagic: " + IntToString(bUseMagic) + - " bUseMagicItems: " + IntToString(bUseMagicItems) + - " nLevel: " + IntToString(nLevel)); - // Loop through nLevels down to nMinNoTalentLevel looking for the first talent - // (i.e. the highest or best?). + if(AI_DEBUG) ai_Debug("0i_talents", "2413", "bUseMagic: " + IntToString(bUseMagic) + + " bUseMagicItems: " + IntToString(bUseMagicItems)); + // Loop through nLevels starting at the highest then going to the lowest. + // (i.e. the highest or best is our assumption). + // Get the saved level from oCreature. + json jLevel = GetLocalJson(oCreature, "SPELL_LEVEL_" + IntToString(nLevel)); + if(AI_DEBUG) ai_Debug("0i_talents", "3091", "jLevel: " + IntToString(nLevel) + " " + JsonDump(jLevel, 2)); + if(JsonGetType(jCategory) == JSON_TYPE_NULL) + { + SetLocalInt(oCreature, AI_MAX_LEVEL + sCategory, -1); + return FALSE; + } while(nLevel > -1) { // Get the array of nLevel cycling down to 0. jLevel = JsonArrayGet(jCategory, nLevel); nMaxSlotIndex = JsonGetLength(jLevel); - if(AI_DEBUG) ai_Debug("0i_talents", "2288", "nLevel: " + IntToString(nLevel) + + if(AI_DEBUG) ai_Debug("0i_talents", "2422", "Level: " + IntToString(nLevel) + " nMaxSlotIndex: " + IntToString(nMaxSlotIndex)); if(nMaxSlotIndex > 0) { + bHasTalent = TRUE; // Get the talent within nLevel cycling from the first to the last. nSlotIndex = 0; while (nSlotIndex < nMaxSlotIndex) { jTalent= JsonArrayGet(jLevel, nSlotIndex); - if(AI_DEBUG) ai_Debug("0i_talents", "2300", "nSlotIndex: " + IntToString(nSlotIndex) + + if(AI_DEBUG) ai_Debug("0i_talents", "2432", "nSlotIndex: " + IntToString(nSlotIndex) + " jTalent Type: " + IntToString(JsonGetInt(JsonArrayGet(jTalent, 0)))); nType = JsonGetInt(JsonArrayGet(jTalent, 0)); if(bUseMagic) @@ -2316,11 +3110,14 @@ int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int n if(nTalentUsed == -1) { ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); return TRUE; } + // There are no more spell slots left for non-memorizing caster so remove the level. else if(nTalentUsed == -2) { ai_RemoveTalentLevel(oCreature, jCategory, jLevel, sCategory, nLevel); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); } else if(nTalentUsed) return TRUE; } @@ -2332,6 +3129,7 @@ int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int n // When the ability is used that slot is now not readied. // Multiple uses of the same spell are stored in different slots. ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); return TRUE; } } @@ -2341,11 +3139,12 @@ int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int n // Items do not need to concentrate. if(ai_UseCreatureItemTalent(oCreature, jLevel, jTalent, sCategory, nInMelee, oTarget)) { - if(AI_DEBUG) ai_Debug("0i_talents", "2337", "Checking if Item is used up: " + + if(AI_DEBUG) ai_Debug("0i_talents", "2473", "Checking if Item is used up: " + IntToString(JsonGetInt(JsonArrayGet(jTalent, 4)))); if(JsonGetInt(JsonArrayGet(jTalent, 4)) == -1) { ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); } return TRUE; } @@ -2354,7 +3153,182 @@ int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int n nSlotIndex++; } } - else SetLocalInt(oCreature, AI_MAX_TALENT + sCategory, nLevel - 1); + else if(!bHasTalent) SetLocalInt(oCreature, AI_MAX_LEVEL + sCategory, nLevel - 1); + nLevel--; + } + return FALSE; +} +int ai_UseTalentByLevel(object oCreature, int nTalent, object oTarget) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1912", GetName(oCreature) + " is trying to use " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nTalent))) + + " on " + GetName(oTarget)); + // Get the saved category from oCreature. + string sCategory = Get2DAString("ai_spells", "Category", nTalent); + json jCategory = GetLocalJson(oCreature, sCategory); + if(AI_DEBUG) ai_Debug("0i_talents", "1917", "jCategory: " + sCategory + " " + JsonDump(jCategory, 2)); + if(JsonGetType(jCategory) == JSON_TYPE_NULL) return FALSE; + json jLevel, jTalent; + int nLevel, nClass, nSlot, nType, nSlotIndex, nMaxSlotIndex, nTalentUsed, nSpell; + // Loop through nLevels down to nMinNoTalentLevel looking for the first talent + // (i.e. the highest or best?). + while(nLevel <= 9) + { + // Get the array of nLevel. + jLevel = JsonArrayGet(jCategory, nLevel); + nMaxSlotIndex = JsonGetLength(jLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "1925", "nLevel: " + IntToString(nLevel) + + " nMaxSlotIndex: " + IntToString(nMaxSlotIndex)); + if(nMaxSlotIndex > 0) + { + // Get the talent within nLevel cycling from the first to the last. + nSlotIndex = 0; + while (nSlotIndex < nMaxSlotIndex) + { + jTalent= JsonArrayGet(jLevel, nSlotIndex); + if(AI_DEBUG) ai_Debug("0i_talents", "1936", "nSlotIndex: " + IntToString(nSlotIndex) + + " jTalent Type: " + IntToString(JsonGetInt(JsonArrayGet(jTalent, 0)))); + nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); + if(nSpell == nTalent) + { + nType = JsonGetInt(JsonArrayGet(jTalent, 0)); + if(nType == AI_TALENT_TYPE_SPELL || nType == AI_TALENT_TYPE_SP_ABILITY) + { + if(ai_UseTalentOnObject(oCreature, jTalent, oTarget, 0)) + { + ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + return TRUE; + } + } + else if(nType == AI_TALENT_TYPE_ITEM) + { + // Items do not need to concentrate. + if(ai_UseCreatureItemTalent(oCreature, jLevel, jTalent, sCategory, 0, oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1955", "Checking if Item is used up: " + + IntToString(JsonGetInt(JsonArrayGet(jTalent, 4)))); + if(JsonGetInt(JsonArrayGet(jTalent, 4)) == -1) + { + ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + } + return TRUE; + } + } + } + nSlotIndex++; + } + } + nLevel++; + } + return FALSE; +} */ +/******************************************************************************* + Old talent system that checks by category then level. +*******************************************************************************/ +int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int nLevel = 10, object oTarget = OBJECT_INVALID) +{ + // Get the Max Talent Level to see if we even need to pull this talent. + int nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + sCategory); + if(AI_DEBUG) ai_Debug("0i_talents", "2394", AI_MAX_TALENT + sCategory + ": " + + IntToString(nMaxTalentLevel) + + " nLevel: " + IntToString(nLevel)); + if(nMaxTalentLevel == -1) return FALSE; + // Get the saved category from oCreature. + json jCategory = GetLocalJson(oCreature, sCategory); + if(AI_DEBUG) ai_Debug("0i_talents", "2326", "jCategory: " + sCategory + " " + JsonDump(jCategory, 2)); + if(JsonGetType(jCategory) == JSON_TYPE_NULL) + { + SetLocalInt(oCreature, AI_MAX_TALENT + sCategory, -1); + return FALSE; + } + // If there are no talents at higher levels then start at the lowest talent level. + if(nMaxTalentLevel < nLevel) nLevel = nMaxTalentLevel; + if(nLevel < 0 || nLevel > 10) nLevel = 9; + json jLevel, jTalent; + int bHasTalent, nClass, nSlot, nType, nSlotIndex, nMaxSlotIndex, nTalentUsed, nSpell; + int bUseMagic = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC); + int bUseMagicItems = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC_ITEMS); + if(AI_DEBUG) ai_Debug("0i_talents", "2413", "bUseMagic: " + IntToString(bUseMagic) + + " bUseMagicItems: " + IntToString(bUseMagicItems)); + // Loop through nLevels starting at the highest then going to the lowest. + // (i.e. the highest or best is our assumption). + while(nLevel > -1) + { + // Get the array of nLevel cycling down to 0. + jLevel = JsonArrayGet(jCategory, nLevel); + nMaxSlotIndex = JsonGetLength(jLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "2422", "nLevel: " + IntToString(nLevel) + + " nMaxSlotIndex: " + IntToString(nMaxSlotIndex)); + if(nMaxSlotIndex > 0) + { + bHasTalent = TRUE; + // Get the talent within nLevel cycling from the first to the last. + nSlotIndex = 0; + while (nSlotIndex < nMaxSlotIndex) + { + jTalent= JsonArrayGet(jLevel, nSlotIndex); + if(AI_DEBUG) ai_Debug("0i_talents", "2432", "nSlotIndex: " + IntToString(nSlotIndex) + + " jTalent Type: " + IntToString(JsonGetInt(JsonArrayGet(jTalent, 0)))); + nType = JsonGetInt(JsonArrayGet(jTalent, 0)); + if(bUseMagic) + { + if(nType == AI_TALENT_TYPE_SPELL) + { + // Check to make sure they still have this spell. + nClass = JsonGetInt(JsonArrayGet(jTalent, 2)); + if(AI_DEBUG) ai_Debug("0i_talents", "3290", GetName(oCreature) + " Memorizes Spells? " + + Get2DAString("classes", "MemorizesSpells", nClass)); + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + if(AI_DEBUG) ai_Debug("0i_talents", "3294", " Spell Memorized? " + + IntToString(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlotIndex))); + if(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlotIndex) < 1) + { + ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); + } + else if(ai_UseCreatureSpellTalent(oCreature, jLevel, jTalent, sCategory, nInMelee, oTarget)) return TRUE; + } + else if(!GetSpellUsesLeft(oCreature, nClass, JsonGetInt(JsonArrayGet(jTalent, 1)))) + { + ai_RemoveTalentLevel(oCreature, jCategory, jLevel, sCategory, nLevel); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); + nTalentUsed = FALSE; + } + else if(ai_UseCreatureSpellTalent(oCreature, jLevel, jTalent, sCategory, nInMelee, oTarget)) return TRUE; + } + else if(nType == AI_TALENT_TYPE_SP_ABILITY) + { + // Special ability spells do not need to concentrate?! + if(ai_CheckSpecialTalentsandUse(oCreature, jTalent, sCategory, nInMelee, oTarget)) + { + // When the ability is used that slot is now not readied. + // Multiple uses of the same spell are stored in different slots. + ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); + return TRUE; + } + } + } + else if(bUseMagicItems && nType == AI_TALENT_TYPE_ITEM) + { + // Items do not need to concentrate. + if(ai_UseCreatureItemTalent(oCreature, jLevel, jTalent, sCategory, nInMelee, oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_talents", "2473", "Checking if Item is used up: " + + IntToString(JsonGetInt(JsonArrayGet(jTalent, 4)))); + if(JsonGetInt(JsonArrayGet(jTalent, 4)) == -1) + { + ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + if(nMaxSlotIndex == 1) ai_UpdateMaxTalentLevel(oCreature, jCategory, sCategory, nMaxTalentLevel, nLevel); + } + return TRUE; + } + } + //else if(nType == AI_TALENT_TYPE_FEAT) {} + nSlotIndex++; + } + } + else if(!bHasTalent) SetLocalInt(oCreature, AI_MAX_TALENT + sCategory, nLevel - 1); nLevel--; } return FALSE; @@ -2422,7 +3396,20 @@ int ai_UseTalent(object oCreature, int nTalent, object oTarget) } return FALSE; } -int ai_UseTalentOnObject(object oCreature, json jTalent, object oTarget, int nInMelee) +// Lets do a check right before we cast the spell to see if the target is still good. +void ai_CheckSpellTarget(object oCreature, object oTarget) +{ + if(GetIsDead(oTarget)) + { + //SendMessageToPC(GetFirstPC(), "0i_talents, 2864, " + GetName(oCreature) + + //" is stopping our spell casting because " + GetName(oTarget) + " is dead!"); + //WriteTimestampedLogEntry("0i_talents, 2864, " + GetName(oCreature) + + //" is stopping our spell casting because " + GetName(oTarget) + " is dead!"); + ai_ClearCreatureActions(); + ExecuteScript("0e_do_combat_rnd", oCreature); + } +} +int ai_UseTalentOnObject(object oCreature, json jTalent, object oTarget, int nInMelee, int bCheckPosition = TRUE) { int nClass, nLevel, nSlot, nMetaMagic, nDomain; int nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); @@ -2451,13 +3438,13 @@ int ai_UseTalentOnObject(object oCreature, json jTalent, object oTarget, int nIn if(AI_DEBUG) ai_Debug("0i_talents", "1790", GetName(oCreature) + " is using a special ability!"); nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); nClass = 255; - if(ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nSpell)) return TRUE; + if(bCheckPosition && ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nSpell)) return TRUE; } else if(nType == AI_TALENT_TYPE_ITEM) { object oItem = StringToObject(JsonGetString(JsonArrayGet(jTalent, 2))); int nBaseItemType = GetBaseItemType(oItem); - if(ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nSpell, nBaseItemType)) return TRUE; + if(bCheckPosition && ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nSpell, nBaseItemType)) return TRUE; int nIndex, nSubIndex = 0; nSlot = JsonGetInt(JsonArrayGet(jTalent, 4)); itemproperty ipProp = GetFirstItemProperty(oItem); @@ -2521,7 +3508,14 @@ int ai_UseTalentOnObject(object oCreature, json jTalent, object oTarget, int nIn " nDomain: " + IntToString(nDomain) + " nClass: " + IntToString(nClass)); ai_SetLastAction(oCreature, nSpell); ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain, 0, FALSE, nClass, FALSE); + // This was added to do a second check right before they cast to make sure + // the target was not killed between us deciding to cast and then casting! + DelayCommand(2.5, ai_CheckSpellTarget(oCreature, oTarget)); ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + // Temp Debug!!!!! + //string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + //SendMessageToPC(GetFirstPC(), "0i_talents, 1859 " + GetName(oCreature) + " is casting " + sSpellName + " on " + GetName(oTarget)); + // Temp Debug!!!!! if(AI_DEBUG) { string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); @@ -2628,7 +3622,23 @@ int ai_UseTalentAtLocation(object oCreature, json jTalent, object oTarget, int n int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategory, int nInMelee, object oTarget) { int nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); - if(AI_DEBUG) ai_Debug("0i_talents", "1949", "nSpell: " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))) + + // This checks to see if they tried to cast this spell last round and if we + // are trying to cast it again before at least 3 seconds is up then the spell + // is being canceled... remove this spell and look for another! + if(ai_CompareLastAction(oCreature, nSpell) && + ai_GetCurrentTimeStamp() - GetLocalInt(oCreature, sLastActionTimeVarname) < 2) + { + if(AI_DEBUG) + { + int nLastTime = GetLocalInt(oCreature, sLastActionTimeVarname); + int nCurrentTime = ai_GetCurrentTimeStamp(); + ai_Debug("0i_talents", "3634", "Same spell cast within less than 2 seconds!" + + " nSpell: " + IntToString(nSpell) + " nLastActionTime: " + IntToString(nLastTime) + + " nCurrentTime: " + IntToString(nCurrentTime) + " Difference: " + IntToString(nCurrentTime - nLastTime)); + } + return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_talents", "3637", "nSpell: " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))) + " sCategory: " + sCategory); if(sCategory == AI_TALENT_DISCRIMINANT_AOE) { @@ -2645,8 +3655,13 @@ int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategor if(nInMelee) fRange = AI_RANGE_MELEE; else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); // Get the biggest group we can. - string sIndex = IntToString(ai_GetHighestMeleeIndexNotInAOE(oCreature)); - oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); + int nIndex = ai_GetHighestGroupIndexNotInAOE(oCreature); + if(nIndex == 0) + { + nIndex = ai_GetHighestGroupIndex(oCreature); + if(nIndex == 0) return FALSE; + } + oTarget = GetLocalObject(oCreature, AI_ENEMY + IntToString(nIndex)); if(!ai_CreatureHasDispelableEffect(oCreature, oTarget)) return FALSE; // Maybe we should do an area of effect instead? int nEnemies = ai_GetNumOfEnemiesInRange(oTarget, 5.0); @@ -2675,15 +3690,46 @@ int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategor if(nUndead < 3) return FALSE; oTarget = ai_GetLowestCRRacialTarget(oCreature, RACIAL_TYPE_UNDEAD, fRange); } + else if(nSpell == SPELLABILITY_PULSE_DROWN) + { + if(nInMelee > 1) + { + oTarget = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + int nRace = GetRacialType(oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "2778", "nRace(10/24/16): " + IntToString(nRace)); + if(nRace != RACIAL_TYPE_CONSTRUCT && + nRace != RACIAL_TYPE_UNDEAD && + nRace != RACIAL_TYPE_ELEMENTAL) + { + if(AI_DEBUG) ai_Debug("0i_talents", "2783", "Using Pulse Drown talent!"); + if(ai_UseTalentOnObject(oCreature, jTalent, oCreature, nInMelee, FALSE)) return TRUE; + } + } + return FALSE; + } + else if(nSpell == SPELLABILITY_PULSE_WHIRLWIND) + { + if(nInMelee > 1) + { + if(AI_DEBUG) ai_Debug("0i_talents", "2793", "Using Pulse Whirlwind talent!"); + if(ai_UseTalentOnObject(oCreature, jTalent, oCreature, nInMelee, FALSE)) return TRUE; + } + return FALSE; + } // Get a target for discriminant spells if one is not already set. if(oTarget == OBJECT_INVALID) { float fRange; if(nInMelee) fRange = AI_RANGE_MELEE; else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); - oTarget = ai_CheckForGroupedTargetNotInAOE(oCreature, fRange); + oTarget = ai_GetGroupTargetNotInAOE(oCreature, fRange); + if(oTarget == OBJECT_INVALID) + { + oTarget = ai_GetGroupTarget(oCreature, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; + } } - if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget) || + if(GetHasSpellEffect(nSpell, oTarget) || !ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; } @@ -2711,9 +3757,13 @@ int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategor if(oTarget == OBJECT_INVALID) { float fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); - oTarget = ai_CheckForGroupedTargetNotInAOE(oCreature, fRange); + oTarget = ai_GetGroupTargetNotInAOE(oCreature, fRange); + if(oTarget == OBJECT_INVALID) + { + oTarget = ai_GetGroupTarget(oCreature, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; + } // Check for the number of allies, if there are too many then skip. - if(oTarget == OBJECT_INVALID) return FALSE; int nRoll = d6() + 1; if(GetAssociateType(oCreature)) nRoll = d3(); int nAllies = ai_GetNumOfAlliesInGroup(oTarget, AI_RANGE_CLOSE); @@ -2760,18 +3810,18 @@ int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategor // Lets get a caster as they should have more buffs. oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER, fRange); // No caster then get the most powerful enemy! - if(oTarget == OBJECT_INVALID) oTarget = ai_GetHighestCRTarget(oCreature, fRange); - if(oTarget != OBJECT_INVALID) + if(oTarget == OBJECT_INVALID) { - if(!ai_CreatureHasDispelableEffect(oCreature, oTarget)) return FALSE; - // Maybe we should do an area of effect instead? - int nEnemies = ai_GetNumOfEnemiesInRange(oTarget, 5.0); - if(nEnemies > 2) - { - if(ai_UseTalentAtLocation(oCreature, jTalent, oTarget, nInMelee)) return TRUE; - } + oTarget = ai_GetHighestCRTarget(oCreature, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; + } + if(!ai_CreatureHasDispelableEffect(oCreature, oTarget)) return FALSE; + // Maybe we should do an area of effect instead? + int nEnemies = ai_GetNumOfEnemiesInRange(oTarget, 5.0); + if(nEnemies > 2) + { + if(ai_UseTalentAtLocation(oCreature, jTalent, oTarget, nInMelee)) return TRUE; } - if(oTarget == OBJECT_INVALID) return FALSE; } // Make sure the spell will work on the target. else if(nSpell == SPELL_HOLD_PERSON || nSpell == SPELL_DOMINATE_PERSON || @@ -2814,8 +3864,9 @@ int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategor if(nInMelee) fRange = AI_RANGE_MELEE; else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); oTarget = ai_GetSpellTargetBasedOnSaves(oCreature, nSpell, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; } - if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget) || + if(GetHasSpellEffect(nSpell, oTarget) || !ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; //********************************************************************** @@ -2857,8 +3908,9 @@ int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategor if(oTarget == OBJECT_INVALID) { oTarget = ai_GetSpellTargetBasedOnSaves(oCreature, nSpell, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) return FALSE; } - if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget) || + if(GetHasSpellEffect(nSpell, oTarget) || !ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; } @@ -3044,8 +4096,9 @@ int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategor if(fRange == 0.1f) oTarget = oCreature; // Range/Touch spell else oTarget = ai_GetAllyBuffTarget(oCreature, nSpell, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; } - if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget)) return FALSE; + if(GetHasSpellEffect(nSpell, oTarget)) return FALSE; //********************************************************************** //********** These spells are checked after picking a target *********** //********************************************************************** diff --git a/src/module/nss/0i_time.nss b/src/module/nss/0i_time.nss index 3052ea6..78d6ec9 100644 --- a/src/module/nss/0i_time.nss +++ b/src/module/nss/0i_time.nss @@ -8,55 +8,42 @@ int GetPosixTimestamp(); string GetCurrentDateTime(); */////////////////////////////////////////////////////////////////////////////// -// RETURNS a Timestamp in seconds since 1970-01-01. -int GetCurrentTimeInSeconds(); -// RETURNS a formated date, good for timestamping logs and text. +// Returns a Timestamp in seconds since 1970-01-01. +int ai_GetCurrentTimeStamp(); +// Returns a formated date, good for Dating logs and text. string GetCurrentDateTime(); // Sends a server shutdown message 1800 seconds i.e 30 minutes before. // nDuration is in seconds. i.e. one hours is 3600 defaults to 24 hours (86400). // Should be put into the servers OnHeartBeat. void CheckServerShutdownMessage(int nDuration = 86400); +/// Returns the current time formatted according to the provided sqlite date time format string. +/// Format string as used by sqlites STRFTIME(). +/// Returns the current time in the requested format. Empty string on error. +string SQLite_GetFormattedSystemTime(string format); +/// Returns the number of milliseconds since midnight on January 1, 1970. +int SQLite_GetTimeMilliseconds(); +/// Returns the date in the format (mm/dd/yyyy). +string SQLite_GetSystemDate(); +/// Returns the current time in the format (24:mm:ss). +string SQLite_GetSystemTime(); -int GetCurrentTimeInSeconds() +int ai_GetCurrentTimeStamp() { - string stmt = "SELECT strftime('%s','now');"; - sqlquery sqlQuery = SqlPrepareQueryObject(GetModule(), stmt); - SqlStep(sqlQuery); - return SqlGetInt(sqlQuery, 0); + sqlquery query = SqlPrepareQueryObject(GetModule(), "SELECT STRFTIME('%s', 'now')"); + SqlStep(query); + return SqlGetInt(query, 0); } string GetCurrentDateTime() { - string stmt = "SELECT datetime('now', 'localtime')"; - sqlquery sqlQuery = SqlPrepareQueryObject(GetModule(), stmt); + sqlquery sqlQuery = SqlPrepareQueryObject(GetModule(), "SELECT datetime('now', 'localtime')"); SqlStep(sqlQuery); return SqlGetString(sqlQuery, 0); } -/// @addtogroup time Time -/// @brief Provides various time related functions. -/// @brief Returns the current time formatted according to the provided sqlite date time format string. -/// @param format Format string as used by sqlites STRFTIME(). -/// @return The current time in the requested format. Empty string on error. -string SQLite_GetFormattedSystemTime(string format); -/// @return Returns the number of seconds since midnight on January 1, 1970. -int SQLite_GetTimeStamp(); -/// @return Returns the number of milliseconds since midnight on January 1, 1970. -int SQLite_GetTimeMilliseconds(); -/// @brief A millisecond timestamp struct SQLite_MillisecondTimeStamp { int seconds; ///< Seconds since epoch int milliseconds; ///< Milliseconds }; -/// @remark For mircosecond timestamps use NWNX_Utility_GetHighResTimeStamp(). -/// @return Returns the number of milliseconds since midnight on January 1, 1970. -struct SQLite_MillisecondTimeStamp SQLite_GetMillisecondTimeStamp(); -/// @brief Returns the current date. -/// @return The date in the format (mm/dd/yyyy). -string SQLite_GetSystemDate(); -/// @brief Returns current time. -/// @return The current time in the format (24:mm:ss). -string SQLite_GetSystemTime(); -/// @} string SQLite_GetFormattedSystemTime(string format) { sqlquery query = SqlPrepareQueryObject(GetModule(), "SELECT STRFTIME(@format, 'now', 'localtime')"); @@ -64,27 +51,12 @@ string SQLite_GetFormattedSystemTime(string format) SqlStep(query); // sqlite returns NULL for invalid format in STRFTIME() return SqlGetString(query, 0); } -int SQLite_GetTimeStamp() -{ - sqlquery query = SqlPrepareQueryObject(GetModule(), "SELECT STRFTIME('%s', 'now')"); - SqlStep(query); - return SqlGetInt(query, 0); -} int SQLite_GetTimeMillisecond() { sqlquery query = SqlPrepareQueryObject(GetModule(), "select cast((julianday('now') - 2440587.5) * 86400 * 1000 as integer)"); SqlStep(query); return SqlGetInt(query, 0); } -struct SQLite_MillisecondTimeStamp SQLite_GetMillisecondTimeStamp() -{ - sqlquery query = SqlPrepareQueryObject(GetModule(), "SELECT STRFTIME('%s', 'now'), SUBSTR(STRFTIME('%f', 'now'), 4)"); - SqlStep(query); - struct SQLite_MillisecondTimeStamp t; - t.seconds = SqlGetInt(query, 0); - t.milliseconds = SqlGetInt(query, 1); - return t; -} string SQLite_GetSystemDate() { return SQLite_GetFormattedSystemTime("%m/%d/%Y"); diff --git a/src/module/nss/ai_a_atk_casters.nss b/src/module/nss/ai_a_atk_casters.nss index 362ba4b..e1a8b4f 100644 --- a/src/module/nss/ai_a_atk_casters.nss +++ b/src/module/nss/ai_a_atk_casters.nss @@ -92,18 +92,18 @@ void main() if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); if(oTarget == OBJECT_INVALID) ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); } else { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); if(oTarget == OBJECT_INVALID) ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; if(AI_DEBUG) ai_Debug("0i_actions", "519", "Do ranged attack against nearest: " + GetName(oTarget) + "!"); ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; @@ -133,7 +133,7 @@ void main() { oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER, AI_RANGE_MELEE, AI_ENEMY, bAlwaysAtk); // If we didn't get a target then get any target within range. - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE, AI_ENEMY, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE, AI_ENEMY, bAlwaysAtk); } // If not then lets go find someone to attack! else @@ -141,7 +141,7 @@ void main() // Get the nearest enemy. oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); // If we didn't get a target then get any target within range. - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); } } } diff --git a/src/module/nss/ai_a_atk_warrior.nss b/src/module/nss/ai_a_atk_warrior.nss index 4821e53..fe6dc71 100644 --- a/src/module/nss/ai_a_atk_warrior.nss +++ b/src/module/nss/ai_a_atk_warrior.nss @@ -92,18 +92,18 @@ void main() if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); if(oTarget == OBJECT_INVALID) ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_WARRIOR); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); } else { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); if(oTarget == OBJECT_INVALID) ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_WARRIOR, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; if(AI_DEBUG) ai_Debug("0i_actions", "519", "Do ranged attack against nearest: " + GetName(oTarget) + "!"); ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; @@ -133,7 +133,7 @@ void main() { oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_WARRIOR, AI_RANGE_MELEE, AI_ENEMY, bAlwaysAtk); // If we didn't get a target then get any target within range. - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE, AI_ENEMY, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE, AI_ENEMY, bAlwaysAtk); } // If not then lets go find someone to attack! else @@ -141,7 +141,7 @@ void main() // Get the nearest enemy. oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_WARRIOR, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); // If we didn't get a target then get any target within range. - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_PERCEPTION, AI_ENEMY, bAlwaysAtk); } } } diff --git a/src/module/nss/ai_a_barbarian.nss b/src/module/nss/ai_a_barbarian.nss index 12521c9..525cfc3 100644 --- a/src/module/nss/ai_a_barbarian.nss +++ b/src/module/nss/ai_a_barbarian.nss @@ -55,12 +55,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_bard.nss b/src/module/nss/ai_a_bard.nss index 975bac2..05166ff 100644 --- a/src/module/nss/ai_a_bard.nss +++ b/src/module/nss/ai_a_bard.nss @@ -52,12 +52,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_cleric.nss b/src/module/nss/ai_a_cleric.nss index 5de1cc2..b138f62 100644 --- a/src/module/nss/ai_a_cleric.nss +++ b/src/module/nss/ai_a_cleric.nss @@ -72,12 +72,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_default.nss b/src/module/nss/ai_a_default.nss index 7540340..6491fdf 100644 --- a/src/module/nss/ai_a_default.nss +++ b/src/module/nss/ai_a_default.nss @@ -54,13 +54,6 @@ void main() if(ai_TryDivineShieldFeat(oCreature, nInMelee)) return; if(ai_TryDivineMightFeat(oCreature, nInMelee)) return; } - //************************** SKILL FEATURES ************************** - if(ai_TryAnimalEmpathy(oCreature)) return; - // ************************** CLASS FEATURES *************************** - if(ai_TryBarbarianRageFeat(oCreature)) return; - if(ai_TryBardSongFeat(oCreature)) return; - if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; - if(ai_TrySummonFamiliarTalent(oCreature)) return; } // Class and Offensive single target talents. if(nDifficulty >= AI_COMBAT_EFFORTLESS) @@ -74,6 +67,17 @@ void main() if(ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; } } + if(nDifficulty >= AI_COMBAT_MODERATE) + { + //************************** SKILL FEATURES ************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + // ************************** CLASS FEATURES *************************** + if(ai_TryPolymorphSelfFeat(oCreature)) return; + if(ai_TryBarbarianRageFeat(oCreature)) return; + if(ai_TryBardSongFeat(oCreature)) return; + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + if(ai_TrySummonFamiliarTalent(oCreature)) return; + } // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. ai_DoPhysicalAttackOnBest(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); } diff --git a/src/module/nss/ai_a_defensive.nss b/src/module/nss/ai_a_defensive.nss index f19523c..983e6e6 100644 --- a/src/module/nss/ai_a_defensive.nss +++ b/src/module/nss/ai_a_defensive.nss @@ -61,7 +61,7 @@ void main() if(ai_TryImprovedExpertiseFeat(oCreature)) return; if(ai_TryExpertiseFeat(oCreature)) return; // Lets get the strongest melee opponent in melee with us. - oTarget = ai_GetHighestCRTarget(oCreature, AI_RANGE_MELEE); + oTarget = ai_GetHighestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget == OBJECT_INVALID) oTarget = oNearestEnemy; // Use knockdown when appropriate and the target is not immune. if(ai_TryKnockdownFeat(oCreature, oTarget)) return; diff --git a/src/module/nss/ai_a_druid.nss b/src/module/nss/ai_a_druid.nss index 6b82c26..ce2d265 100644 --- a/src/module/nss/ai_a_druid.nss +++ b/src/module/nss/ai_a_druid.nss @@ -17,8 +17,6 @@ void main() if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; int nDifficulty = ai_GetDifficulty(oCreature); int nMaxLevel; - //************************** SKILL FEATURES ************************** - if(ai_TryAnimalEmpathy(oCreature)) return; // Check for moral and get the maximum spell level we should use. if(nDifficulty >= AI_COMBAT_EFFORTLESS) { @@ -26,13 +24,38 @@ void main() nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); } // Skill, Class, Offensive AOE's, and Defensive talents. + object oTarget = OBJECT_INVALID; + // Get the Spell Level we should still cast before turning into our polymorph form. + int nSpellLevel = ai_GetHasPolymorphSelfFeat(oCreature); + int nMaxTalentLevel; + if(AI_DEBUG) ai_Debug("ai_a_druid", "31", "nSpellLevel: " + IntToString(nSpellLevel)); if(nDifficulty >= AI_COMBAT_MODERATE) { + // *************************** SPELL TALENTS *************************** + if(!ai_GetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING)) + { + if(ai_UseCreatureTalent(oCreature, AI_TALENT_INDISCRIMINANT_AOE, nInMelee, nMaxLevel)) return; + if(ai_UseCreatureTalent(oCreature, AI_TALENT_DISCRIMINANT_AOE, nInMelee, nMaxLevel)) return; + } // ************************** CLASS FEATURES *************************** if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; - if(ai_TryPolymorphSelfFeat(oCreature)) return; - // *************************** SPELL TALENTS *************************** - if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + //************************** DEFENSIVE TALENTS *************************** + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_SUMMON); + if(AI_DEBUG) ai_Debug("ai_a_druid", "47", "nMaxTalentLevel 'S' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_SUMMON, nInMelee, nMaxLevel, oTarget)) return; + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_PROTECTION); + if(AI_DEBUG) ai_Debug("ai_a_druid", "51", "nMaxTalentLevel 'P' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_PROTECTION, nInMelee, nMaxLevel, oTarget)) return; + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_ENHANCEMENT); + if(AI_DEBUG) ai_Debug("ai_a_druid", "55", "nMaxTalentLevel 'E' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_ENHANCEMENT, nInMelee, nMaxLevel, oTarget)) return; + } } // Offensive single target talents. if(nDifficulty >= AI_COMBAT_EFFORTLESS) @@ -40,13 +63,24 @@ void main() // *************************** SPELL TALENTS *************************** if(!ai_GetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING)) { - if(nInMelee > 0 && ai_UseCreatureTalent(oCreature, AI_TALENT_TOUCH, nInMelee, nMaxLevel)) return; - if(ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; + if(nInMelee > 0) + { + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_TOUCH); + if(AI_DEBUG) ai_Debug("ai_druid", "69", "nMaxTalentLevel 'T' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_TOUCH, nInMelee, nMaxLevel)) return; + } + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_RANGED); + if(AI_DEBUG) ai_Debug("ai_druid", "74", "nMaxTalentLevel 'R' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; } + if(nDifficulty >= AI_COMBAT_MODERATE && ai_TryPolymorphSelfFeat(oCreature)) return; } + //************************** SKILL FEATURES ************************** + if(ai_TryAnimalEmpathy(oCreature)) return; // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. // ************************** Ranged feat attacks ************************** - object oTarget; if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) { if(ai_HasRangedWeaponWithAmmo(oCreature)) @@ -56,12 +90,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_fighter.nss b/src/module/nss/ai_a_fighter.nss index a5f3720..5434180 100644 --- a/src/module/nss/ai_a_fighter.nss +++ b/src/module/nss/ai_a_fighter.nss @@ -51,12 +51,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_flanker.nss b/src/module/nss/ai_a_flanker.nss index fb6845a..16fdbcb 100644 --- a/src/module/nss/ai_a_flanker.nss +++ b/src/module/nss/ai_a_flanker.nss @@ -60,8 +60,8 @@ void main() { oTarget = ai_GetFlankTarget(oCreature, AI_RANGE_MELEE); } - // Ok we are in a serious fight so lets not give attack of opportunities. - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + // Ok we are in a serious fight so lets not give attacks of opportunities. + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); } // If there are no enemies being attacked then lets stay back. if(oTarget == OBJECT_INVALID) @@ -83,10 +83,10 @@ void main() if(ai_HasRangedWeaponWithAmmo(oCreature)) { if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; - oTarget = ai_GetLowestCRTarget(oCreature); + oTarget = ai_GetLowestCRPhysicalTarget(oCreature); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } @@ -98,6 +98,12 @@ void main() return; } } + // Make sure we are not the only one here. Moving around looks funny when we are by ourselves. + else if(ai_GetNearestAlly(oCreature, 1, 7, 7) == OBJECT_INVALID) + { + oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } } if(oTarget != OBJECT_INVALID) { diff --git a/src/module/nss/ai_a_invisible.nss b/src/module/nss/ai_a_invisible.nss index 9772b32..a660c84 100644 --- a/src/module/nss/ai_a_invisible.nss +++ b/src/module/nss/ai_a_invisible.nss @@ -75,12 +75,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_monk.nss b/src/module/nss/ai_a_monk.nss index 607ce4c..a690c16 100644 --- a/src/module/nss/ai_a_monk.nss +++ b/src/module/nss/ai_a_monk.nss @@ -52,12 +52,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_no_cmb_mode.nss b/src/module/nss/ai_a_no_cmb_mode.nss index 1ffedb4..5a3981c 100644 --- a/src/module/nss/ai_a_no_cmb_mode.nss +++ b/src/module/nss/ai_a_no_cmb_mode.nss @@ -91,17 +91,17 @@ void main() { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); } else { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; if(AI_DEBUG) ai_Debug("ai_a_no_modes", "105", GetName(OBJECT_SELF) + " does ranged attack on weakest: " + GetName(oTarget) + "!"); ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; diff --git a/src/module/nss/ai_a_paladin.nss b/src/module/nss/ai_a_paladin.nss index 24520a7..7e2391a 100644 --- a/src/module/nss/ai_a_paladin.nss +++ b/src/module/nss/ai_a_paladin.nss @@ -73,12 +73,12 @@ void main() if(oTarget == OBJECT_INVALID) { // Paladins face off against the strongest opponents first. - if(!nInMelee) oTarget = ai_GetHighestCRTarget(oCreature); - else oTarget = ai_GetHighestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetHighestCRPhysicalTarget(oCreature); + else oTarget = ai_GetHighestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_polymorphed.nss b/src/module/nss/ai_a_polymorphed.nss index 107e6e9..e566632 100644 --- a/src/module/nss/ai_a_polymorphed.nss +++ b/src/module/nss/ai_a_polymorphed.nss @@ -8,16 +8,21 @@ // Programmer: Philos ////////////////////////////////////////////////////////////////////////////////////////////////////// #include "0i_actions" -void ai_DoActions(object oCreature, int nForm) +void main() { + object oCreature = OBJECT_SELF; int nInMelee = ai_GetNumOfEnemiesInRange(oCreature); object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); if(ai_TryHealingTalent(oCreature, nInMelee)) return; if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; if(GetPercentageHPLoss(oCreature) <= AI_HEALTH_BLOODY) { - //ai_Debug("ai_a_polymorphed", "24", "We are wounded and are transforming back!"); + if(AI_DEBUG) ai_Debug("ai_a_polymorphed", "20", "We are wounded and are transforming back!"); ai_RemoveASpecificEffect(oCreature, EFFECT_TYPE_POLYMORPH); + DeleteLocalInt(oCreature, AI_POLYMORPHED); + // We need to create the creatures normal forms talent list. + DelayCommand(0.0, ai_ClearTalents(oCreature)); + DelayCommand(0.1, ai_SetCreatureTalents(oCreature, FALSE, TRUE)); return; } int nDifficulty = ai_GetDifficulty(oCreature); @@ -51,20 +56,3 @@ void ai_DoActions(object oCreature, int nForm) if(oTarget != OBJECT_INVALID) ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); else ai_SearchForHiddenCreature(oCreature, FALSE); } -void main() -{ - object oCreature = OBJECT_SELF; - // Need to know who we are so we can use thier abilities. - int nForm = GetAppearanceType(oCreature); - // Check to see if we are back to our normal form?(-1 to get the actual form #) - if(nForm == GetLocalInt(oCreature, AI_NORMAL_FORM) - 1) - { - // If we are transformed back then go back to our primary ai. - ai_SetCreatureAIScript(oCreature); - DeleteLocalInt(oCreature, AI_NORMAL_FORM); - string sAI = GetLocalString(oCreature, AI_COMBAT_SCRIPT); - if(sAI == "ai_a_polymorphed" || sAI == "") sAI = "ai_a_default"; - ExecuteScript(sAI, oCreature); - } - else ai_DoActions(oCreature, nForm); -} diff --git a/src/module/nss/ai_a_ranged.nss b/src/module/nss/ai_a_ranged.nss index 477937d..5b98865 100644 --- a/src/module/nss/ai_a_ranged.nss +++ b/src/module/nss/ai_a_ranged.nss @@ -68,17 +68,17 @@ void main() if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); if(oTarget == OBJECT_INVALID) oTarget == ai_GetRangedTarget(oCreature); if(oTarget == OBJECT_INVALID && ai_TryRangedSneakAttack(oCreature, nInMelee)) return; - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); } else { if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } @@ -88,7 +88,30 @@ void main() return; } } - else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + else + { + if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + oTarget = ai_GetEnemyAttackingMe(oCreature); + if(oTarget != OBJECT_INVALID) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + else + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + } + } } } // ************************** Melee feat attacks ************************* @@ -102,7 +125,12 @@ void main() if(ai_TrySneakAttack(oCreature, nInMelee)) return; if(ai_TryWhirlwindFeat(oCreature)) return; if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + else + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); if(oTarget != OBJECT_INVALID) { diff --git a/src/module/nss/ai_a_ranger.nss b/src/module/nss/ai_a_ranger.nss index 179298f..b359a5b 100644 --- a/src/module/nss/ai_a_ranger.nss +++ b/src/module/nss/ai_a_ranger.nss @@ -59,17 +59,17 @@ void main() if(!nInMelee) { oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); } else { oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_rogue.nss b/src/module/nss/ai_a_rogue.nss index 10e1ae0..ad7c290 100644 --- a/src/module/nss/ai_a_rogue.nss +++ b/src/module/nss/ai_a_rogue.nss @@ -52,12 +52,12 @@ void main() { if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_a_sorcerer.nss b/src/module/nss/ai_a_sorcerer.nss index 5e4a3b7..ac5062a 100644 --- a/src/module/nss/ai_a_sorcerer.nss +++ b/src/module/nss/ai_a_sorcerer.nss @@ -50,8 +50,8 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { diff --git a/src/module/nss/ai_a_wizard.nss b/src/module/nss/ai_a_wizard.nss index dfad8c3..0249788 100644 --- a/src/module/nss/ai_a_wizard.nss +++ b/src/module/nss/ai_a_wizard.nss @@ -52,8 +52,8 @@ void main() if(oTarget == OBJECT_INVALID) { // Lets pick off the weakest targets. - if(!nInMelee) oTarget = ai_GetLowestCRTarget(oCreature); - else oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetLowestCRPhysicalTarget(oCreature); + else oTarget = ai_GetLowestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { diff --git a/src/module/nss/ai_barbarian.nss b/src/module/nss/ai_barbarian.nss index 69efcb4..56604ba 100644 --- a/src/module/nss/ai_barbarian.nss +++ b/src/module/nss/ai_barbarian.nss @@ -41,11 +41,11 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_bard.nss b/src/module/nss/ai_bard.nss index 9dde198..d26236c 100644 --- a/src/module/nss/ai_bard.nss +++ b/src/module/nss/ai_bard.nss @@ -39,11 +39,11 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_bloodmane.nss b/src/module/nss/ai_bloodmane.nss new file mode 100644 index 0000000..456fbe4 --- /dev/null +++ b/src/module/nss/ai_bloodmane.nss @@ -0,0 +1,38 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_bloodmane +////////////////////////////////////////////////////////////////////////////////////////////////////// + AI combat action scripts for Bloodmane - Orc Warlord(Barbarian - Example). + To use this AI set the variable string "AI_DEFAULT_SCRIPT" to "ai_bloodmane" on the creature. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +void main() +{ + object oCreature = OBJECT_SELF; + //************************************************************************** + //************************ START SPECIAL AI SCRIPTS ************************ + //************************************************************************** + int nRound = ai_GetCurrentRound(oCreature); + // First round cuss and animate! + if(nRound == 1) + { + // Make him taunt the player! + ActionPlayAnimation(ANIMATION_FIREFORGET_TAUNT); + PlayVoiceChat(Random(4), oCreature); + } + // Second round go into a Rage. + else if(nRound == 2) + { + // Use Rage! + if(ai_TryBarbarianRageFeat(oCreature)) return; + // If for some reason he doesn't have a rage then charge into melee! + object oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, ai_GetNumOfEnemiesInRange(oCreature)); + if(oTarget != OBJECT_INVALID) ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + // Change Bloodmane's ai to Barbarian since we are done with his special ai. + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_barbarian"); + } + //************************************************************************** + //************************ END SPECIAL AI SCRIPTS ************************** + //************************************************************************** +} diff --git a/src/module/nss/ai_cleric.nss b/src/module/nss/ai_cleric.nss index 34bd1e6..9b256c0 100644 --- a/src/module/nss/ai_cleric.nss +++ b/src/module/nss/ai_cleric.nss @@ -41,8 +41,8 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); diff --git a/src/module/nss/ai_default.nss b/src/module/nss/ai_default.nss index cfe8e8f..f84f6f7 100644 --- a/src/module/nss/ai_default.nss +++ b/src/module/nss/ai_default.nss @@ -26,15 +26,11 @@ void main() //**************************** SKILL FEATURES **************************** if(ai_TryAnimalEmpathy(oCreature)) return; //**************************** CLASS FEATURES **************************** - if(ai_TryBarbarianRageFeat(oCreature)) return; - if(ai_TryBardSongFeat(oCreature)) return; - if(ai_TryTurningTalent(oCreature)) return; if(GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS)) { if(ai_TrySummonFamiliarTalent(oCreature)) return; if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; } - if(ai_TryPolymorphSelfFeat(oCreature)) return; //************************** DEFENSIVE TALENTS *************************** int nRound = ai_GetCurrentRound(oCreature); if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; @@ -44,6 +40,11 @@ void main() // Look for a touch attack since we are in melee. if(nInMelee > 0 && ai_UseCreatureTalent(oCreature, AI_TALENT_TOUCH, nInMelee, nMaxLevel)) return; if(ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; + //**************************** CLASS FEATURES **************************** + if(ai_TryPolymorphSelfFeat(oCreature)) return; + if(ai_TryBarbarianRageFeat(oCreature)) return; + if(ai_TryBardSongFeat(oCreature)) return; + if(ai_TryTurningTalent(oCreature)) return; // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. ai_DoPhysicalAttackOnNearest(oCreature, nInMelee); } diff --git a/src/module/nss/ai_dragon_boss.nss b/src/module/nss/ai_dragon_boss.nss new file mode 100644 index 0000000..fc85f9b --- /dev/null +++ b/src/module/nss/ai_dragon_boss.nss @@ -0,0 +1,103 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_dragon_boss +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script a unique dragon that lives deep in a dark cave using it as a defense. + OBJECT_SELF is the dragon running the ai. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +void main() +{ + object oCreature = OBJECT_SELF; + // Get the number of enemies that we are in melee combat with. + int nInMelee = ai_GetNumOfEnemiesInRange(oCreature); + //************************************************************************** + //************************ ROUND BASED AI SCRIPTS ************************* + //************************************************************************** + int nRound = ai_GetCurrentRound(oCreature); + // First time fly to our enemy, the rest of combat lets not do that! + object oTarget; + if(!GetLocalInt(OBJECT_SELF, "AI_DONE_FLYING")) + { + SetLocalInt(OBJECT_SELF, "AI_DONE_FLYING", TRUE); + oTarget = ai_GetLowestCRTarget(oCreature); + // We assign the voice to the PC so they get to hear it. + object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC); + AssignCommand(oPC, PlaySound("vs_ndredm_bat2")); + // Can we do a crush attack(HD 18+)? + if(ai_TryCrushAttack(oCreature, oTarget)) return; + ai_FlyToTarget(oCreature, oTarget); + return; + } + else if(nRound == 2) + { + oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_CLOSE); + ai_TryDragonBreathAttack(oCreature, nRound, oTarget); + return; + } + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + // Check to see if we need to retreat to get healing. + int nPercentageHP = ai_GetPercHPLoss(oCreature); + //ai_Debug("ai_dragon_boss", "43", "nPercentageHP: " + IntToString(nPercentageHP)); + if(nPercentageHP < 75 && !GetLocalInt(oCreature, "AI_HOME")) + { + string sWaypoint; + // If we are below half then go to last defensive position. + if(nPercentageHP < 50) + { + SetLocalInt(oCreature, "AI_HOME", TRUE); + sWaypoint = "0_wp_dragon2"; + } + // else we just go back a little bit to heal up. + else sWaypoint = "0_wp_dragon1"; + if(!GetLocalInt(oCreature, sWaypoint)) + { + string sVoice; + switch(d6()) + { + case 1 : + case 2 : sVoice = "vs_ndredm_attk"; break; + case 3 :sVoice = "vs_ndredm_heal"; break; + case 4 :sVoice = "vs_ndredm_help"; break; + case 5 :sVoice = "vs_ndredm_no"; break; + case 6 :sVoice = "vs_ndredm_bat3"; break; + } + SetImmortal(oCreature, TRUE); + DelayCommand(6.0f, SetImmortal(oCreature, FALSE)); + AssignCommand(ai_GetNearestTarget(oCreature), PlaySound(sVoice)); + object oWaypoint = GetNearestObjectByTag(sWaypoint); + //ai_Debug("ai_dragon_boss", "71", "Flying to " + sWaypoint + "."); + effect eFly = EffectDisappearAppear(GetLocation(oWaypoint)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFly, oCreature, 6.0f); + SetLocalInt(oCreature, sWaypoint, TRUE); + return; + } + } + int nMaxLevel = ai_GetMonsterTalentMaxLevel(oCreature); + //******************* OFFENSIVE AREA OF EFFECT TALENTS ******************* + // Check the battlefield for a group of enemies to shoot a big talent at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + if(ai_UseCreatureTalent(oCreature, AI_TALENT_INDISCRIMINANT_AOE, nInMelee, nMaxLevel)) return; + if(ai_UseCreatureTalent(oCreature, AI_TALENT_DISCRIMINANT_AOE, nInMelee, nMaxLevel)) return; + //************************** DEFENSIVE TALENTS *************************** + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; + //********************** OFFENSIVE TARGETED TALENTS ********************** + // Look for a touch attack since we are in melee. + if(nInMelee > 0 && ai_UseCreatureTalent(oCreature, AI_TALENT_TOUCH, nInMelee, nMaxLevel)) return; + if(ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; + // ************************ MELEE ATTACKS ******************************** + oTarget = ai_GetLowestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryDragonBreathAttack(oCreature, nRound)) return; + ai_TryWingAttacks(oCreature); + // If we don't do a Tail sweep attack(HD 30+) then see if we can do a Tail slap(HD 12+)! + if(!ai_TryTailSweepAttack(oCreature)) ai_TryTailSlap(oCreature); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/src/module/nss/ai_druid.nss b/src/module/nss/ai_druid.nss index 95cb82b..aebbb0e 100644 --- a/src/module/nss/ai_druid.nss +++ b/src/module/nss/ai_druid.nss @@ -22,18 +22,40 @@ void main() // to take advantage of them as often as possible. if(ai_UseCreatureTalent(oCreature, AI_TALENT_INDISCRIMINANT_AOE, nInMelee, nMaxLevel)) return; if(ai_UseCreatureTalent(oCreature, AI_TALENT_DISCRIMINANT_AOE, nInMelee, nMaxLevel)) return; - //**************************** SKILL FEATURES **************************** - if(ai_TryAnimalEmpathy(oCreature)) return; //**************************** CLASS FEATURES **************************** if(GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS) && ai_TrySummonAnimalCompanionTalent(oCreature)) return; - if(ai_TryPolymorphSelfFeat(oCreature)) return; //************************** DEFENSIVE TALENTS *************************** - int nRound = ai_GetCurrentRound(oCreature); - if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; + // Get the Spell Level we should still cast before turning into our polymorph form. + int nSpellLevel = ai_GetHasPolymorphSelfFeat(oCreature); + if(AI_DEBUG) ai_Debug("ai_druid", "30", "nSpellLevel: " + IntToString(nSpellLevel)); + int nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_SUMMON); + if(AI_DEBUG) ai_Debug("ai_druid", "32", "nMaxTalentLevel 'S' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_SUMMON, nInMelee, nMaxLevel)) return; + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_PROTECTION); + if(AI_DEBUG) ai_Debug("ai_druid", "36", "nMaxTalentLevel 'P' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_PROTECTION, nInMelee, nMaxLevel)) return; + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_ENHANCEMENT); + if(AI_DEBUG) ai_Debug("ai_druid", "40", "nMaxTalentLevel 'E' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_ENHANCEMENT, nInMelee, nMaxLevel)) return; //********************** OFFENSIVE TARGETED TALENTS ********************** // Look for a touch attack since we are in melee. - if(nInMelee > 0 && ai_UseCreatureTalent(oCreature, AI_TALENT_TOUCH, nInMelee, nMaxLevel)) return; - if(ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; + if(nInMelee > 0) + { + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_TOUCH); + if(AI_DEBUG) ai_Debug("ai_druid", "48", "nMaxTalentLevel 'T' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_TOUCH, nInMelee, nMaxLevel)) return; + } + nMaxTalentLevel = GetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_RANGED); + if(AI_DEBUG) ai_Debug("ai_druid", "53", "nMaxTalentLevel 'R' " + IntToString(nMaxTalentLevel)); + if(nSpellLevel < nMaxTalentLevel && + ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; + if(ai_TryPolymorphSelfFeat(oCreature)) return; + //**************************** SKILL FEATURES **************************** + if(ai_TryAnimalEmpathy(oCreature)) return; // All else fails lets see if we have any good potions. // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. // ************************ RANGED ATTACKS ******************************* @@ -43,10 +65,11 @@ void main() if(ai_HasRangedWeaponWithAmmo(oCreature)) { // Lets pick off the nearest targets. - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_fighter.nss b/src/module/nss/ai_fighter.nss index 04f4ce0..9602923 100644 --- a/src/module/nss/ai_fighter.nss +++ b/src/module/nss/ai_fighter.nss @@ -36,11 +36,11 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } @@ -54,7 +54,7 @@ void main() } // **************************** MELEE ATTACKS **************************** if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; - if(ai_TryWhirlwindFeat (oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; oTarget = ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee); if (oTarget != OBJECT_INVALID) { diff --git a/src/module/nss/ai_flanker.nss b/src/module/nss/ai_flanker.nss index 20a6538..86fc7a3 100644 --- a/src/module/nss/ai_flanker.nss +++ b/src/module/nss/ai_flanker.nss @@ -53,7 +53,7 @@ void main() oTarget = ai_GetFlankTarget(oCreature, AI_RANGE_MELEE); } // Ok we are in a serious fight so lets not give attack of opportunities. - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); } // If there are no enemies being attacked then lets stay back. if(oTarget == OBJECT_INVALID) @@ -75,10 +75,10 @@ void main() if(ai_HasRangedWeaponWithAmmo(oCreature)) { if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; - oTarget = ai_GetNearestTarget(oCreature); + oTarget = ai_GetNearestPhysicalTarget(oCreature); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_incorporeal.nss b/src/module/nss/ai_incorporeal.nss index cdd20bd..14c1a76 100644 --- a/src/module/nss/ai_incorporeal.nss +++ b/src/module/nss/ai_incorporeal.nss @@ -44,8 +44,8 @@ void main() { if (ai_TryRangedSneakAttack (oCreature, nInMelee)) return; string sIndex; - if (!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget (oCreature, AI_RANGE_MELEE); + if (!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget (oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { if(ai_TryRapidShotFeat (oCreature, oTarget, nInMelee)) return; @@ -63,19 +63,21 @@ void main() // ************************* MELEE ATTACKS ******************************* if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; oTarget = ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee); - if (oTarget != OBJECT_INVALID) + if(oTarget != OBJECT_INVALID) { // If we are using our hands then do a touch attack instead. - if (GetItemInSlot (INVENTORY_SLOT_RIGHTHAND) == OBJECT_INVALID) + if(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND) == OBJECT_INVALID) { - if (GetItemInSlot (INVENTORY_SLOT_CWEAPON_L) != OBJECT_INVALID) + // If they don't have a claw then we need to do a special attack instead. + if(GetItemInSlot(INVENTORY_SLOT_CWEAPON_L) != OBJECT_INVALID) { // Randomize so they don't appear synchronized. float fDelay = IntToFloat(Random(2) + 1); DelayCommand(fDelay, ActionCastSpellAtObject (769/*Shadow_Attack*/, oTarget, METAMAGIC_ANY, TRUE)); ai_SetLastAction(oCreature, AI_LAST_ACTION_MELEE_ATK); - SetLocalObject (oCreature, AI_ATTACKED_PHYSICAL, oTarget); + SetLocalObject(oCreature, AI_ATTACKED_PHYSICAL, oTarget); } + else ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); } else ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); } diff --git a/src/module/nss/ai_invisible.nss b/src/module/nss/ai_invisible.nss index 7d42b21..7d8863a 100644 --- a/src/module/nss/ai_invisible.nss +++ b/src/module/nss/ai_invisible.nss @@ -47,11 +47,11 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_monk.nss b/src/module/nss/ai_monk.nss index d6ff1b6..d087073 100644 --- a/src/module/nss/ai_monk.nss +++ b/src/module/nss/ai_monk.nss @@ -38,8 +38,8 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if (!nInMelee) oTarget = ai_GetNearestTarget (oCreature); - else oTarget = ai_GetNearestTarget (oCreature, AI_RANGE_MELEE); + if (!nInMelee) oTarget = ai_GetNearestPhysicalTarget (oCreature); + else oTarget = ai_GetNearestPhysicalTarget (oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); diff --git a/src/module/nss/ai_paladin.nss b/src/module/nss/ai_paladin.nss index d45d223..46efe88 100644 --- a/src/module/nss/ai_paladin.nss +++ b/src/module/nss/ai_paladin.nss @@ -43,8 +43,8 @@ void main() if(ai_HasRangedWeaponWithAmmo(oCreature)) { // Paladins face the biggest challenges first! - if(!nInMelee) oTarget = ai_GetHighestCRTarget(oCreature); - else oTarget = ai_GetHighestCRTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetHighestCRPhysicalTarget(oCreature); + else oTarget = ai_GetHighestCRPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); diff --git a/src/module/nss/ai_polymorphed.nss b/src/module/nss/ai_polymorphed.nss index 9a7630a..6c1ab1f 100644 --- a/src/module/nss/ai_polymorphed.nss +++ b/src/module/nss/ai_polymorphed.nss @@ -8,16 +8,21 @@ // Programmer: Philos ////////////////////////////////////////////////////////////////////////////////////////////////////// #include "0i_actions" -void ai_DoActions(object oCreature, int nForm) +void main() { + object oCreature = OBJECT_SELF; int nInMelee = ai_GetNumOfEnemiesInRange(oCreature); object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); if(ai_TryHealingTalent(oCreature, nInMelee)) return; if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; if(GetPercentageHPLoss(oCreature) <= AI_HEALTH_BLOODY) { - if(AI_DEBUG) ai_Debug("ai_polymorphed", "19", "We are wounded and are transforming back!"); + if(AI_DEBUG) ai_Debug("ai_polymorphed", "20", "We are wounded and are transforming back!"); ai_RemoveASpecificEffect(oCreature, EFFECT_TYPE_POLYMORPH); + DeleteLocalInt(oCreature, AI_POLYMORPHED); + // We need to create the creatures normal forms talent list. + DelayCommand(0.0, ai_ClearTalents(oCreature)); + DelayCommand(0.1, ai_SetCreatureTalents(oCreature, TRUE, TRUE)); return; } int nMaxLevel = ai_GetMonsterTalentMaxLevel(oCreature); @@ -31,25 +36,30 @@ void ai_DoActions(object oCreature, int nForm) if(nInMelee > 0 && ai_UseCreatureTalent(oCreature, AI_TALENT_TOUCH, nInMelee, nMaxLevel)) return; if(ai_UseCreatureTalent(oCreature, AI_TALENT_RANGED, nInMelee, nMaxLevel)) return; // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. - object oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); - // If we don't find a target then we don't want to fight anyone! + // *************************** RANGED ATTACKS **************************** + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // **************************** MELEE ATTACKS **************************** + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); if(oTarget != OBJECT_INVALID) ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); else ai_SearchForHiddenCreature(oCreature, TRUE); } -void main() -{ - object oCreature = OBJECT_SELF; - // Need to know who we are so we can use thier abilities. - int nForm = GetAppearanceType(oCreature); - // Check to see if we are back to our normal form?(-1 to get the actual form #) - if(nForm == GetLocalInt(oCreature, AI_NORMAL_FORM) - 1) - { - // If we are transformed back then go back to our primary ai. - ai_SetCreatureAIScript(oCreature); - DeleteLocalInt(oCreature, AI_NORMAL_FORM); - string sAI = GetLocalString(oCreature, AI_COMBAT_SCRIPT); - if(sAI == "ai_polymorphed" || sAI == "") sAI = "ai_default"; - ExecuteScript(sAI, oCreature); - } - else ai_DoActions(oCreature, nForm); -} diff --git a/src/module/nss/ai_ranged.nss b/src/module/nss/ai_ranged.nss index 3a46228..d64fc0a 100644 --- a/src/module/nss/ai_ranged.nss +++ b/src/module/nss/ai_ranged.nss @@ -59,16 +59,16 @@ void main() if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); if(oTarget == OBJECT_INVALID) oTarget == ai_GetRangedTarget(oCreature); if(oTarget == OBJECT_INVALID && ai_TryRangedSneakAttack(oCreature, nInMelee)) return; - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature); } else { if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } @@ -78,7 +78,30 @@ void main() return; } } - else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + else + { + if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + oTarget = ai_GetEnemyAttackingMe(oCreature); + if(oTarget != OBJECT_INVALID) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + else + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + } + } } // ************************** Melee feat attacks ************************* object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); @@ -90,7 +113,12 @@ void main() if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; if(ai_TrySneakAttack(oCreature, nInMelee)) return; if(ai_TryWhirlwindFeat(oCreature)) return; - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + else + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); if(oTarget != OBJECT_INVALID) { diff --git a/src/module/nss/ai_ranger.nss b/src/module/nss/ai_ranger.nss index 5878cfb..08a6d5a 100644 --- a/src/module/nss/ai_ranger.nss +++ b/src/module/nss/ai_ranger.nss @@ -45,16 +45,16 @@ void main() if(!nInMelee) { oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature); } else { oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); - if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); } if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_rogue.nss b/src/module/nss/ai_rogue.nss index 81c4500..ed270d4 100644 --- a/src/module/nss/ai_rogue.nss +++ b/src/module/nss/ai_rogue.nss @@ -38,7 +38,7 @@ void main() if(ai_HasRangedWeaponWithAmmo(oCreature)) { if (ai_TryRangedSneakAttack (oCreature, nInMelee)) return; - oTarget = ai_GetNearestTarget (oCreature); + oTarget = ai_GetNearestPhysicalTarget (oCreature); if(oTarget != OBJECT_INVALID) { if (ai_TryRapidShotFeat (oCreature, oTarget, nInMelee)) return; diff --git a/src/module/nss/ai_shadow.nss b/src/module/nss/ai_shadow.nss index 3d6419f..e708d27 100644 --- a/src/module/nss/ai_shadow.nss +++ b/src/module/nss/ai_shadow.nss @@ -38,11 +38,11 @@ void main() if(ai_HasRangedWeaponWithAmmo(oCreature)) { string sIndex; - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_sorcerer.nss b/src/module/nss/ai_sorcerer.nss index f1fdcc3..afbf502 100644 --- a/src/module/nss/ai_sorcerer.nss +++ b/src/module/nss/ai_sorcerer.nss @@ -37,8 +37,8 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); diff --git a/src/module/nss/ai_taunter.nss b/src/module/nss/ai_taunter.nss index 825d025..89aae52 100644 --- a/src/module/nss/ai_taunter.nss +++ b/src/module/nss/ai_taunter.nss @@ -49,11 +49,11 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if (!nInMelee) oTarget = ai_GetNearestTarget (oCreature); - else oTarget = ai_GetNearestTarget (oCreature, AI_RANGE_MELEE); + if (!nInMelee) oTarget = ai_GetNearestPhysicalTarget (oCreature); + else oTarget = ai_GetNearestPhysicalTarget (oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { - if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + if(ai_TryRangedTalents(oCreature, oTarget, nInMelee)) return; ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); return; } diff --git a/src/module/nss/ai_wizard.nss b/src/module/nss/ai_wizard.nss index 6baa4c8..082cde4 100644 --- a/src/module/nss/ai_wizard.nss +++ b/src/module/nss/ai_wizard.nss @@ -39,8 +39,8 @@ void main() { if(ai_HasRangedWeaponWithAmmo(oCreature)) { - if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); - else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(!nInMelee) oTarget = ai_GetNearestPhysicalTarget(oCreature); + else oTarget = ai_GetNearestPhysicalTarget(oCreature, AI_RANGE_MELEE); if(oTarget != OBJECT_INVALID) { ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); diff --git a/src/module/nss/mm_prc_spells.nss b/src/module/nss/mm_prc_spells.nss index 4aeb8e0..9b542bc 100644 --- a/src/module/nss/mm_prc_spells.nss +++ b/src/module/nss/mm_prc_spells.nss @@ -3,7 +3,7 @@ int StartingUp(object oPC); json ai_CheckToReplaceSpell(json jSpellList, int nClass, int nLevel, int nSlot) { - //if(d100() > 49) return jSpellList; + if(d100() > 49) return jSpellList; string sSpellTableColumn = Get2DAString("classes", "SpellTableColumn", nClass); int nRoll = d10() + 1 + nLevel * 10; int nSpell = StringToInt(Get2DAString("prc_add_spells", sSpellTableColumn, nRoll)); diff --git a/src/module/nss/nw_c2_default1.nss b/src/module/nss/nw_c2_default1.nss index 4801a4d..fc8f361 100644 --- a/src/module/nss/nw_c2_default1.nss +++ b/src/module/nss/nw_c2_default1.nss @@ -10,10 +10,8 @@ void main() { // If not runnning normal or better AI then exit for performance reasons if (GetAILevel(OBJECT_SELF) == AI_LEVEL_VERY_LOW) return; - - ExecuteScript("prc_npc_hb", OBJECT_SELF); - object oCreature = OBJECT_SELF; + ExecuteScript("prc_npc_hb", oCreature); if(AI_DEBUG) ai_Debug("nw_c2_default1", "16", GetName(oCreature) + " Heartbeat." + " OnSpawn: " + IntToString(GetLocalInt(oCreature, AI_ONSPAWN_EVENT))); // We run our OnSpawn in the heartbeat so the creator can use the original @@ -37,6 +35,7 @@ void main() } } // Send the user-defined event signal if specified here so it doesn't get skipped. + //SendMessageToPC(GetFirstPC(), "HB_EVENT: " + IntToString(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT))); if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT)) { SignalEvent(oCreature, EventUserDefined(EVENT_HEARTBEAT)); @@ -78,17 +77,9 @@ void main() ai_ClearBuffTargets(oCreature, "AI_ALLY_TARGET_"); } } - if(!IsInConversation (oCreature)) - { - if(GetWalkCondition(NW_WALK_FLAG_CONSTANT)) WalkWayPoints(); - if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS)) PlayMobileAmbientAnimations_NonAvian(); - else if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN)) PlayMobileAmbientAnimations_Avian(); - else if(GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS)) PlayImmobileAmbientAnimations(); - else if(GetLocalInt(GetModule(), AI_RULE_WANDER) && GetStandardFactionReputation(STANDARD_FACTION_HOSTILE, oCreature) > 89) - { - ai_AmbientAnimations(); - } - } if(ai_TryHealing(oCreature, oCreature)) return; + // This is where PEPS passes control to either the Bioware Waypoint system and + // AmbientAnimation scripts. A Persistent World can write there own scripts in 0e_animations. + ExecuteScript("0e_animations", oCreature); } diff --git a/src/module/nss/nw_c2_default2.nss b/src/module/nss/nw_c2_default2.nss index b7d5e07..fa6f5f8 100644 --- a/src/module/nss/nw_c2_default2.nss +++ b/src/module/nss/nw_c2_default2.nss @@ -16,7 +16,7 @@ void main() // * if not runnning normal or better AI then exit for performance reasons //if (GetAILevel() == AI_LEVEL_VERY_LOW) return; object oCreature = OBJECT_SELF; - ExecuteScript("prc_npc_percep", OBJECT_SELF); + ExecuteScript("prc_npc_percep", oCreature); if(AI_DEBUG) ai_Debug("nw_c2_default2", "19", "AI_ONSPAWN_EVENT: " + IntToString(GetLocalInt(oCreature, AI_ONSPAWN_EVENT))); if(!GetLocalInt(oCreature, AI_ONSPAWN_EVENT)) return; if(GetLastPerceptionSeen()) diff --git a/src/module/nss/nw_c2_default3.nss b/src/module/nss/nw_c2_default3.nss index 567b327..d95a483 100644 --- a/src/module/nss/nw_c2_default3.nss +++ b/src/module/nss/nw_c2_default3.nss @@ -17,12 +17,12 @@ void main() { object oCreature = OBJECT_SELF; - ExecuteScript("prc_npc_combat", OBJECT_SELF); + ExecuteScript("prc_npc_combat", oCreature); if(AI_DEBUG) ai_Debug("nw_c2_default3", "20", GetName(oCreature) + " ends combat round." + " Current action: " + IntToString(GetCurrentAction(oCreature))); if(GetSpawnInCondition(NW_FLAG_END_COMBAT_ROUND_EVENT)) { - SignalEvent(OBJECT_SELF, EventUserDefined(1003)); + SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_END_COMBAT_ROUND)); } if(ai_Disabled(oCreature)) return; // Action modes get cleared prior to each OnCombatRoundEnd! @@ -59,7 +59,7 @@ void main() } if(ai_GetIsInCombat(oCreature)) { - ai_DoAssociateCombatRound (oCreature); + ai_DoMonsterCombatRound (oCreature); return; } if(ai_GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) ai_DetermineSpecialBehavior(oCreature); diff --git a/src/module/nss/nw_c2_default4.nss b/src/module/nss/nw_c2_default4.nss index cd1f616..c7df7e2 100644 --- a/src/module/nss/nw_c2_default4.nss +++ b/src/module/nss/nw_c2_default4.nss @@ -12,7 +12,8 @@ void ai_MonsterCommands(object oCreature, object oSpeaker, int nMatch); void main() { object oCreature = OBJECT_SELF; - ExecuteScript("prc_npc_conv", OBJECT_SELF); + object oLastSpeaker = GetLastSpeaker(); + ExecuteScript("prc_npc_conv", oCreature); if(AI_DEBUG) ai_Debug("nw_c2_default4", "15", GetName(oCreature) + " listens " + IntToString(GetListenPatternNumber()) + " to " + GetName(GetLastSpeaker()) + "." + " AI_AM_I_SEARCHING: " + IntToString(GetLocalInt(oCreature, AI_AM_I_SEARCHING))); @@ -22,7 +23,7 @@ void main() ai_DoMonsterCombatRound(oCreature); return; } - object oLastSpeaker = GetLastSpeaker(); + //object oLastSpeaker = GetLastSpeaker(); int nMatch = GetListenPatternNumber(); if(nMatch != -1) { diff --git a/src/module/nss/nw_c2_default5.nss b/src/module/nss/nw_c2_default5.nss index acc3b3e..c2e663d 100644 --- a/src/module/nss/nw_c2_default5.nss +++ b/src/module/nss/nw_c2_default5.nss @@ -10,7 +10,7 @@ void main() { object oCreature = OBJECT_SELF; - ExecuteScript("prc_npc_physatt", OBJECT_SELF); + ExecuteScript("prc_npc_physatt", oCreature); object oAttacker = GetLastAttacker(oCreature); if(AI_DEBUG) ai_Debug("nw_c2_default5", "14", GetName(oCreature) + " was attacked by " + GetName(oAttacker) + "."); diff --git a/src/module/nss/nw_c2_default6.nss b/src/module/nss/nw_c2_default6.nss index 5347842..60aef49 100644 --- a/src/module/nss/nw_c2_default6.nss +++ b/src/module/nss/nw_c2_default6.nss @@ -13,9 +13,7 @@ void main() { object oCreature = OBJECT_SELF; - - ExecuteScript("prc_npc_damaged", OBJECT_SELF); - + ExecuteScript("prc_npc_damaged", oCreature); // Send the user-defined event signal if(GetSpawnInCondition(NW_FLAG_DAMAGED_EVENT)) { diff --git a/src/module/nss/nw_c2_default8.nss b/src/module/nss/nw_c2_default8.nss index fa7ef11..ef07c73 100644 --- a/src/module/nss/nw_c2_default8.nss +++ b/src/module/nss/nw_c2_default8.nss @@ -10,9 +10,9 @@ #include "0i_actions" void main() { - ExecuteScript("prc_npc_disturb", OBJECT_SELF); - - if(AI_DEBUG) ai_Debug("nw_c2_default8", "13", GetName(OBJECT_SELF) + " is been disturbed!"); + object oCreature = OBJECT_SELF; + ExecuteScript("prc_npc_disturb", oCreature); + if(AI_DEBUG) ai_Debug("nw_c2_default8", "13", GetName(oCreature) + " is been disturbed!"); // We do nothing at the moment... lets not mess up our factions ok? // This should be defined by the server admins and is commented out. //if(ai_GetIsBusy(OBJECT_SELF, FALSE) || ai_Disabled()) return; @@ -21,6 +21,6 @@ void main() // Send the disturbed flag if appropriate. if(GetSpawnInCondition(NW_FLAG_DISTURBED_EVENT)) { - SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_DISTURBED)); + SignalEvent(oCreature, EventUserDefined(EVENT_DISTURBED)); } } diff --git a/src/module/nss/nw_c2_defaultb.nss b/src/module/nss/nw_c2_defaultb.nss index 3150026..050a3ea 100644 --- a/src/module/nss/nw_c2_defaultb.nss +++ b/src/module/nss/nw_c2_defaultb.nss @@ -10,10 +10,10 @@ void main() { object oCreature = OBJECT_SELF; + ExecuteScript("prc_npc_spellat", oCreature); object oCaster = GetLastSpellCaster(); SetLocalObject(oCaster, AI_ATTACKED_SPELL, oCreature); if(ai_Disabled(oCreature)) return; - ExecuteScript("prc_npc_spellat", OBJECT_SELF); if(!GetLastSpellHarmful()) return; // Send the user-defined event as appropriate if(GetSpawnInCondition(NW_FLAG_SPELL_CAST_AT_EVENT)) diff --git a/src/module/nss/nw_c2_defaulte.nss b/src/module/nss/nw_c2_defaulte.nss index 76f62db..98e4364 100644 --- a/src/module/nss/nw_c2_defaulte.nss +++ b/src/module/nss/nw_c2_defaulte.nss @@ -9,6 +9,7 @@ void main() { object oCreature = OBJECT_SELF; + ExecuteScript("prc_npc_blocked", oCreature); // This actually gets either a Creature or Door that is blocking OBJECT_SELF. object oObject = GetBlockingDoor(); if(AI_DEBUG) ai_Debug("nw_c2_defaulte", "14", GetName(oCreature) + " is being blocked by " + GetName(oObject)); diff --git a/src/module/nss/nw_ch_ac1.nss b/src/module/nss/nw_ch_ac1.nss index 6ed2fea..5bae2be 100644 --- a/src/module/nss/nw_ch_ac1.nss +++ b/src/module/nss/nw_ch_ac1.nss @@ -59,6 +59,8 @@ void main() string sAssociateType = ai_GetAssociateType(oMaster, oCreature); ai_CheckAssociateData(oMaster, oCreature, sAssociateType); ai_CheckPCStart(oMaster); + // When a henchman dies and is brought back the plot flag can be set to TRUE! + SetPlotFlag(oCreature, FALSE); if(AI_HENCHMAN_WIDGET) { // This keeps widgets from disappearing and reappearing. diff --git a/src/module/nss/nw_ch_ac3.nss b/src/module/nss/nw_ch_ac3.nss index 9eb3406..2141565 100644 --- a/src/module/nss/nw_ch_ac3.nss +++ b/src/module/nss/nw_ch_ac3.nss @@ -37,6 +37,14 @@ void main() case ACTION_CASTSPELL : case ACTION_ITEMCASTSPELL : case ACTION_COUNTERSPELL : return; + case ACTION_ATTACKOBJECT : + { + if(!ai_GetIsInCombat(oCreature)) + { + ai_ClearCreatureActions(); + return; + } + } // Might be doing a special action that is not a defined action. case ACTION_INVALID : { diff --git a/src/module/nss/nw_ch_acb.nss b/src/module/nss/nw_ch_acb.nss index ec96e77..9030505 100644 --- a/src/module/nss/nw_ch_acb.nss +++ b/src/module/nss/nw_ch_acb.nss @@ -27,7 +27,10 @@ void main() int nSpell = GetLastSpell(); if(AI_DEBUG) ai_Debug("nw_ch_acb", "21", GetName(OBJECT_SELF) + " has been hit by a harmful spell(" + Get2DAString("spells", "Label", nSpell) + ")!"); - if(ai_GetInAOEReaction(oCreature, oCaster, nSpell) && + object oMaster = GetMaster(oCreature); + if((!GetLocalInt(oMaster, AI_TARGET_MODE_ON) || + GetLocalObject(oMaster, AI_TARGET_MODE_ASSOCIATE) != oCreature) && + ai_GetInAOEReaction(oCreature, oCaster, nSpell) && ai_IsInADangerousAOE(oCreature, AI_RANGE_BATTLEFIELD, TRUE)) return; if(ai_GetIsBusy(oCreature)) return; if(ai_CheckForCombat(oCreature, FALSE)) return; diff --git a/src/module/nss/pc_savebuffs.nss b/src/module/nss/pc_savebuffs.nss index 96b42d0..00e6401 100644 --- a/src/module/nss/pc_savebuffs.nss +++ b/src/module/nss/pc_savebuffs.nss @@ -26,73 +26,82 @@ json GetBuffDatabaseJson(object oPlayer, string sDataField, string sTag = ""); // Returns the level if this spell has a domain spell on nLevel, or 0. int GetHasDomainSpell(object oCaster, int nClass, int nLevel, int nSpell); -// We do some crazy hack to get all the correct information when casting spells. -// GetLastSpellCastClass() will only give the class if this script is running -// on the actual caster, i.e. our PC. -// GetLastSpellLevel() will only give the level if this script is running on -// the actual caster, i.e. our PC. -// So for this to work we run this scrip in the event OnSpellCastAt of our -// target, then we ExecuteScript this script again with the Caster (oPC) -// as OBJECT_SELF for this script on its second pass. This allows us to get the -// information from the above functions! Neat! void main() { object oTarget = OBJECT_SELF; - // The first pass we get oCaster via GetLastSpellCaster() fails in ExecuteScript! - // The second pass we get oCaster via the variable "AI_BUFF_CASTER". - object oCaster = GetLocalObject(oTarget, "AI_BUFF_CASTER"); - if(oCaster == OBJECT_INVALID) oCaster = GetLastSpellCaster(); - // We setting up the save spells button we saved the PC to itself. - // Here we get the PC to make sure the caster of this spell is our saving PC. - object oPC = GetLocalObject(oCaster, "AI_BUFF_PC"); - // The first pass we get nspell via GetLastSpell() fails in ExecuteScript! - // The second pass we get nSpell via the variable "AI_BUFF_SPELL". - int nSpell = GetLocalInt(oTarget, "AI_BUFF_SPELL"); - if(nSpell == 0) nSpell = GetLastSpell(); - // If this is a harful spell or The caster does not equal our saving PC then - // we need to fix the targets scripts back and run the correct OnSpellCastAt script. - if(GetLastSpellHarmful() || oPC != oCaster) + object oCaster = GetLastSpellCaster(); + // When setting up the save spells button we saved the PC to itself. + // Here we get the PC from either our henchmen or ourselves. + // We do this to make sure that this PC and henchmen are the ones saving spells. + object oPC = GetLocalObject(ai_GetPlayerMaster(oCaster), "AI_BUFF_PC"); + // If this is a harmful spell or we couldn't find oPC then we need to fix + // the targets scripts back and run the correct OnSpellCastAt script. + if(GetLastSpellHarmful() || oPC == OBJECT_INVALID) { - string sScript = GetLocalString(oTarget, "AI_BUFF_CAST_AT_SCRIPT"); - SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, sScript); + DeleteLocalObject(oPC, "AI_BUFF_PC"); + string sScript = GetLocalString(oPC, "AI_BUFF_CAST_AT_SCRIPT"); + SetEventScript(oPC, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, sScript); + DeleteLocalString(oPC, "AI_BUFF_CAST_AT_SCRIPT"); + // Cleanup your followers to allow spells to be reacted to as normal. + int nAssociateType = 2; + object oAssociate = GetAssociate(nAssociateType, oPC); + while(nAssociateType < 5) + { + if(oAssociate != OBJECT_INVALID) + { + sScript = GetLocalString(oAssociate, "AI_BUFF_CAST_AT_SCRIPT"); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, sScript); + DeleteLocalString(oAssociate, "AI_BUFF_CAST_AT_SCRIPT"); + } + oAssociate = GetAssociate(++nAssociateType, oPC); + } + int nIndex = 1; + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + while(nIndex <= AI_MAX_HENCHMAN) + { + if(oAssociate != OBJECT_INVALID) + { + sScript = GetLocalString(oAssociate, "AI_BUFF_CAST_AT_SCRIPT"); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, sScript); + DeleteLocalString(oAssociate, "AI_BUFF_CAST_AT_SCRIPT"); + } + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, ++nIndex); + } + NuiSetBind(oPC, NuiFindWindow(oPC, "widgetbuffwin"), "btn_save", JsonBool(FALSE)); + ai_SendMessages("Saving spells to the list has been turned off.", AI_COLOR_YELLOW, oPC); ExecuteScript(sScript, oTarget); return; } - // If the oTarget != oCaster then we are casting a spell on one of our - // associates. We must make a second pass to get the correct information. - // We do this by saving the Target, Caster, and Spell so we can get them - // in the second pass as Execute Script makes them impossible to get on a - // second pass. - if(oTarget != oCaster) - { - SetLocalObject(oPC, "AI_BUFF_TARGET", oTarget); - SetLocalObject(oPC, "AI_BUFF_CASTER", oCaster); - SetLocalInt(oPC, "AI_BUFF_SPELL", nSpell); - ExecuteScript("pc_savebuffs", oPC); - return; - } - // If this is the first pass and we get here then oCaster is casting a spell - // on themselves. So oTarget will be invalid and we should use oPC. - // If this is the second pass and we get here then we have saved oTarget - // to oPC and this will get them so we can save the target to the spell! - oTarget = GetLocalObject(oPC, "AI_BUFF_TARGET"); - if(oTarget == OBJECT_INVALID) oTarget = oPC; - // We need to clean up this mess! - DeleteLocalObject(oPC, "AI_BUFF_TARGET"); - DeleteLocalObject(oPC, "AI_BUFF_CASTER"); - DeleteLocalInt(oPC, "AI_BUFF_SPELL"); // This blocks one spell from saving multiple times due to being an AOE. if(GetLocalInt(oPC, "AI_ONLY_ONE")) return; SetLocalInt(oPC, "AI_ONLY_ONE", TRUE); // We delay this for just less than half a round due to haste. DelayCommand(2.5, DeleteLocalInt(oPC, "AI_ONLY_ONE")); - // Here is the whole problem and why we must do a second pass if the target - // is not the caster. These only work if this script is run by the caster. - int nClass = GetLastSpellCastClass(); - int nLevel = GetLastSpellLevel(); - // Everything below saves the spell to the database with all our now correct info. - int nDomain = GetHasDomainSpell(oPC, nClass, nLevel, nSpell); - int nMetaMagic = GetMetaMagicFeat(); + // If the oTarget != oCaster then we are casting a spell on one of our + // associates. Some functions expect OBJECT_SELF to be the caster. + // We get around that by doing some ExecuteScriptChunk shenanigans. + int nClass, nLevel, nMetaMagic; + if(oTarget != oCaster) + { + // These functions need the caster to be OBJECT_SELF so lets do a HACK! + ExecuteScriptChunk("SetLocalInt(OBJECT_SELF, \"AI_BUFF_CASTCLASS\", GetLastSpellCastClass());", oCaster); + ExecuteScriptChunk("SetLocalInt(OBJECT_SELF, \"AI_BUFF_SPELLLEVEL\", GetLastSpellLevel());", oCaster); + ExecuteScriptChunk("SetLocalInt(OBJECT_SELF, \"AI_BUFF_METAMAGIC\", GetMetaMagicFeat());", oCaster); + nClass = GetLocalInt(oCaster, "AI_BUFF_CASTCLASS"); + nLevel = GetLocalInt(oCaster, "AI_BUFF_SPELLLEVEL"); + nMetaMagic = GetLocalInt(oCaster, "AI_METAMAGIC"); + DeleteLocalInt(oCaster, "AI_BUFF_CASTCLASS"); + DeleteLocalInt(oCaster, "AI_BUFF_SPELLLEVEL"); + DeleteLocalInt(oCaster, "AI_BUFF_METAMAGIC"); + } + else + { + nClass = GetLastSpellCastClass(); + nLevel = GetLastSpellLevel(); + nMetaMagic = GetMetaMagicFeat(); + } + int nSpell = GetLastSpell(); + int nDomain = GetHasDomainSpell(oCaster, nClass, nLevel, nSpell); string sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); if(nDomain) sName += " [Domain]"; if(nMetaMagic > 0 && StringToInt(Get2DAString("classes", "MemorizesSpells", nClass))) @@ -114,11 +123,13 @@ void main() jSpell = JsonArrayInsert(jSpell, JsonInt(nLevel)); jSpell = JsonArrayInsert(jSpell, JsonInt(nMetaMagic)); jSpell = JsonArrayInsert(jSpell, JsonInt(nDomain)); - string sTargetName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oTarget, TRUE))); + string sCasterName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oCaster))); + jSpell = JsonArrayInsert(jSpell, JsonString(sCasterName)); + string sTargetName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oTarget))); jSpell = JsonArrayInsert(jSpell, JsonString(sTargetName)); - jSpell = JsonArrayInsert(jSpells, jSpell); + jSpells = JsonArrayInsert(jSpells, jSpell); SetBuffDatabaseJson(oPC, "spells", jSpells, sList); - SendMessageToPC(oPC, sName + " has been saved for fast buffing on " + sTargetName + "."); + SendMessageToPC(oPC, sCasterName + " has cast " + sName + " to be saved for fast buffing on " + sTargetName + "."); ExecuteScript("pi_buffing", oPC); } string GetBuffDatabaseString(object oPlayer, string sDataField, string sTag) diff --git a/src/module/nss/pe_buffing.nss b/src/module/nss/pe_buffing.nss index a6c4050..8f3262e 100644 --- a/src/module/nss/pe_buffing.nss +++ b/src/module/nss/pe_buffing.nss @@ -45,24 +45,22 @@ void main() // Watch to see if the window moves and save. if(sElem == "window_geometry" && sEvent == "watch") { - if(!GetLocalInt (oPC, AI_NO_NUI_SAVE)) + if(GetLocalInt (oPC, AI_NO_NUI_SAVE)) return; + // Get the height, width, x, and y of the window. + json jGeom = NuiGetBind(oPC, nToken, "window_geometry"); + // Save on the player using the sWndId. + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + if(sWndId == "plbuffwin") { - // Get the height, width, x, and y of the window. - json jGeom = NuiGetBind(oPC, nToken, "window_geometry"); - // Save on the player using the sWndId. - json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); - if(sWndId == "plbuffwin") - { - jMenuData = JsonArraySet(jMenuData, 1, JsonObjectGet(jGeom, "x")); - jMenuData = JsonArraySet(jMenuData, 2, JsonObjectGet(jGeom, "y")); - } - else if(sWndId == "widgetbuffwin") - { - jMenuData = JsonArraySet(jMenuData, 5, JsonObjectGet(jGeom, "x")); - jMenuData = JsonArraySet(jMenuData, 6, JsonObjectGet(jGeom, "y")); - } - SetBuffDatabaseJson(oPC, "spells", jMenuData, "menudata"); + jMenuData = JsonArraySet(jMenuData, 1, JsonObjectGet(jGeom, "x")); + jMenuData = JsonArraySet(jMenuData, 2, JsonObjectGet(jGeom, "y")); } + else if(sWndId == "widgetbuffwin") + { + jMenuData = JsonArraySet(jMenuData, 5, JsonObjectGet(jGeom, "x")); + jMenuData = JsonArraySet(jMenuData, 6, JsonObjectGet(jGeom, "y")); + } + SetBuffDatabaseJson(oPC, "spells", jMenuData, "menudata"); return; } //************************************************************************** @@ -175,6 +173,7 @@ void main() } else if(sEvent == "watch") { + if(GetLocalInt (oPC, AI_NO_NUI_SAVE)) return; if(sElem == "buff_widget_check") { int bBuffWidget = JsonGetInt(NuiGetBind(oPC, nToken, "buff_widget_check")); @@ -184,7 +183,7 @@ void main() if(bBuffWidget) PopupWidgetBuffGUIPanel(oPC); else NuiDestroy(oPC, NuiFindWindow(oPC, "widgetbuffwin")); } - if(sElem == "lock_buff_widget_check") + else if(sElem == "lock_buff_widget_check") { int bBuffLockWidget = JsonGetInt(NuiGetBind(oPC, nToken, "lock_buff_widget_check")); json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); @@ -194,11 +193,20 @@ void main() NuiSetBind(oPC, nToken, "buff_widget_check", JsonBool(TRUE)); PopupWidgetBuffGUIPanel(oPC); } - if(sElem == "chbx_no_monster_check_check") + else if(sElem == "chbx_no_monster_check_check") { int bNoCheckMonsters = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); SetLocalInt(oPC, FB_NO_MONSTER_CHECK, bNoCheckMonsters); } + else if(sElem == "txt_spell_delay") + { + string sDelay = JsonGetString(NuiGetBind(oPC, nToken, "txt_spell_delay")); + float fDelay = StringToFloat(sDelay); + if(fDelay < 0.1f) fDelay = 0.1f; + if(fDelay > 6.0f) fDelay = 6.0f; + sDelay = FloatToString(fDelay, 0, 1); + SetBuffDatabaseString(oPC, "spells", sDelay, "Delay"); + } } } //************************************************************************** @@ -259,13 +267,12 @@ json GetBuffDatabaseJson (object oPlayer, string sDataField, string sTag) if(SqlStep(sql)) return SqlGetJson(sql, 0); else return JsonArray(); } -void CastBuffSpell (object oPC, object oTarget, int nSpell, int nClass, int nMetamagic, int nDomain, string sList, string sName) +void CastBuffSpell(object oPC, object oCaster, object oTarget, int nSpell, int nClass, int nMetamagic, int nDomain, string sList, string sName, int bInstantSpell) { - string sTargetName; - if(oPC == oTarget) sTargetName = "myself."; - else sTargetName = GetName(oTarget); - ai_SendMessages("Quick Buffing: " + sName + " on " + sTargetName, AI_COLOR_GREEN, oPC); - AssignCommand(oPC, ActionCastSpellAtObject(nSpell, oTarget, nMetamagic, FALSE, nDomain, 0, TRUE, nClass)); + string sCasterName = GetName(oCaster); + string sTargetName = GetName(oTarget); + ai_SendMessages(sCasterName + " is quick buffing " + sName + " on " + sTargetName, AI_COLOR_GREEN, oPC); + AssignCommand(oCaster, ActionCastSpellAtObject(nSpell, oTarget, nMetamagic, FALSE, nDomain, 0, bInstantSpell, nClass)); } void CastSavedBuffSpells(object oPC) { @@ -314,7 +321,10 @@ void CastSavedBuffSpells(object oPC) if(fDistance > 30.0f || fDistance == 0.0) { string sName; - float fDelay = 0.1f; + float fDelay; + float fDelayIncrement = StringToFloat(GetBuffDatabaseString(oPC, "spells", "Delay"));; + int bInstantSpell; + if(fDelayIncrement < 3.0f) bInstantSpell = TRUE; int nSpell, nClass, nLevel, nMetamagic, nDomain, nSpellReady, nIndex = 0; json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); string sList = JsonGetString(JsonArrayGet(jMenuData, 0)); @@ -329,58 +339,78 @@ void CastSavedBuffSpells(object oPC) nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); nMetamagic = JsonGetInt(JsonArrayGet(jSpell, 3)); nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); - // We save the target's name then look them up by it. - string sTargetName = JsonGetString(JsonArrayGet(jSpell, 5)); - object oTarget; - location lLocation = GetLocation(oPC); - if(sTargetName == "" || sTargetName == ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName (oPC)))) oTarget = oPC; - else - { - oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 10.0, lLocation, TRUE); - while(oTarget != OBJECT_INVALID) - { - if(sTargetName == ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oTarget)))) break; - oTarget = GetNextObjectInShape(SHAPE_SPHERE, 10.0, lLocation, TRUE); - } - } sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - if(oTarget == OBJECT_INVALID) + location lLocation = GetLocation(oPC); + // Saved the Caster's name so we can find them to cast the spell. + string sCasterName = JsonGetString(JsonArrayGet(jSpell, 5)); + object oCaster; + if(sCasterName == "" || sCasterName == ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPC)))) oCaster = oPC; + else { - DelayCommand(fDelay, ai_SendMessages("Cannot quick cast " + sName + " because the " + sTargetName + " is not here!", AI_COLOR_RED, oPC)); + oCaster = GetFirstObjectInShape(SHAPE_SPHERE, 20.0, lLocation, TRUE); + while(oCaster != OBJECT_INVALID) + { + if(sCasterName == ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oCaster)))) break; + oCaster = GetNextObjectInShape(SHAPE_SPHERE, 20.0, lLocation, TRUE); + } + } + if(oCaster == OBJECT_INVALID) + { + DelayCommand(fDelay, ai_SendMessages("Cannot quick cast " + sName + " because the " + sCasterName + " is not here!", AI_COLOR_RED, oPC)); } else { - if(nMetamagic > 0) + // Saved the target's name so we can find them to cast the spell on. + string sTargetName = JsonGetString(JsonArrayGet(jSpell, 6)); + object oTarget; + if(sTargetName == "" || sTargetName == ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPC)))) oTarget = oPC; + else { - if(nMetamagic == METAMAGIC_EMPOWER) sName += " (Empowered)"; - else if(nMetamagic == METAMAGIC_EXTEND) sName += " (Extended)"; - else if(nMetamagic == METAMAGIC_MAXIMIZE) sName += " (Maximized)"; - else if(nMetamagic == METAMAGIC_QUICKEN) sName += " (Quickened)"; - else if(nMetamagic == METAMAGIC_SILENT) sName += " (Silent)"; - else if(nMetamagic == METAMAGIC_STILL) sName += " (Still)"; + oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 20.0, lLocation, TRUE); + while(oTarget != OBJECT_INVALID) + { + if(sTargetName == ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oTarget)))) break; + oTarget = GetNextObjectInShape(SHAPE_SPHERE, 20.0, lLocation, TRUE); + } } - nSpellReady = GetSpellReady(oPC, nSpell, nClass, nLevel, nMetamagic, nDomain); - if(nSpellReady == TRUE) + if(oTarget == OBJECT_INVALID) { - DelayCommand(fDelay, CastBuffSpell(oPC, oTarget, nSpell, nClass, nMetamagic, nDomain, sList, sName)); + DelayCommand(fDelay, ai_SendMessages("Cannot quick cast " + sName + " because the " + sTargetName + " is not here!", AI_COLOR_RED, oPC)); } - else if(nSpellReady == -1) + else { - DelayCommand(fDelay, ai_SendMessages("Cannot quick cast " + sName + " because it is not ready to cast!", AI_COLOR_RED, oPC)); + if(nMetamagic > 0) + { + if(nMetamagic == METAMAGIC_EMPOWER) sName += " (Empowered)"; + else if(nMetamagic == METAMAGIC_EXTEND) sName += " (Extended)"; + else if(nMetamagic == METAMAGIC_MAXIMIZE) sName += " (Maximized)"; + else if(nMetamagic == METAMAGIC_QUICKEN) sName += " (Quickened)"; + else if(nMetamagic == METAMAGIC_SILENT) sName += " (Silent)"; + else if(nMetamagic == METAMAGIC_STILL) sName += " (Still)"; + } + nSpellReady = GetSpellReady(oCaster, nSpell, nClass, nLevel, nMetamagic, nDomain); + if(nSpellReady == TRUE) + { + DelayCommand(fDelay, CastBuffSpell(oPC, oCaster, oTarget, nSpell, nClass, nMetamagic, nDomain, sList, sName, bInstantSpell)); + } + else if(nSpellReady == -1) + { + DelayCommand(fDelay, ai_SendMessages(sCasterName + " cannot quick cast " + sName + " because it is not ready to cast!", AI_COLOR_RED, oPC)); + } + else if(nSpellReady == -2) + { + DelayCommand (fDelay, ai_SendMessages(sCasterName + " cannot quick cast " + sName + " because it is not memorized!", AI_COLOR_RED, oPC)); + } + else if(nSpellReady == -3) + { + DelayCommand (fDelay, ai_SendMessages(sCasterName + " cannot quick cast " + sName + " because there are no spell slots of that level left!", AI_COLOR_RED, oPC)); + } + else if(nSpellReady == -4) + { + DelayCommand (fDelay, ai_SendMessages(sCasterName + "cannot quick cast " + sName + " because that spell is not known.", AI_COLOR_RED, oPC)); + } + fDelay += fDelayIncrement; } - else if(nSpellReady == -2) - { - DelayCommand (fDelay, ai_SendMessages("Cannot quick cast " + sName + " because it is not memorized!", AI_COLOR_RED, oPC)); - } - else if(nSpellReady == -3) - { - DelayCommand (fDelay, ai_SendMessages("Cannot quick cast " + sName + " because there are no spell slots of that level left!", AI_COLOR_RED, oPC)); - } - else if(nSpellReady == -4) - { - DelayCommand (fDelay, ai_SendMessages("Cannot quick cast " + sName + " because that spell is not known.", AI_COLOR_RED, oPC)); - } - fDelay += 0.1f; } } else break; @@ -397,6 +427,7 @@ int GetSpellReady(object oCaster, int nSpell, int nClass, int nLevel, int nMetam if(StringToInt(Get2DAString("classes", "MemorizesSpells", nClass))) { int nSpellMemorized; + nMaxIndex = GetMemorizedSpellCountByLevel(oCaster, nClass, nLevel); while(nIndex < nMaxIndex) { nMSpell = GetMemorizedSpellId(oCaster, nClass, nLevel, nIndex); @@ -404,20 +435,18 @@ int GetSpellReady(object oCaster, int nSpell, int nClass, int nLevel, int nMetam { nMmSpell = GetMemorizedSpellMetaMagic(oCaster, nClass, nLevel, nIndex); nDSpell = GetMemorizedSpellIsDomainSpell(oCaster, nClass, nLevel, nIndex); - //ai_Debug("pe_buffing", "308", "nMmSpell: " + IntToString(nMmSpell) + + //SendMessageToPC(oCaster, "pe_buffing, 308, nSpell: " + IntToString(nSpell) + + // " nMSpell: " + IntToString(nMSpell) + + // " nMmSpell: " + IntToString(nMmSpell) + // " nMetamagic: " + IntToString(nMetamagic) + // " nDomain: " + IntToString(nDomain) + // " nDSpell: " + IntToString(nDSpell)); - // Cannot save the domain status so we just use the first spell ID. - // Then return the domain statusl. - //if(nMmSpell == nMetamagic && - // ((nDomain > 0 && nDSpell == TRUE) || nDomain == 0 && nDSpell == FALSE)) if(nMmSpell == nMetamagic) { nSpellMemorized = TRUE; if(GetMemorizedSpellReady(oCaster, nClass, nLevel, nIndex)) { - if(nDSpell == nDomain) return TRUE; + if((nDomain && nDSpell) || (!nDomain && !nDSpell)) return TRUE; } } } @@ -425,18 +454,20 @@ int GetSpellReady(object oCaster, int nSpell, int nClass, int nLevel, int nMetam { sSubRadSpell = "SubRadSpell" + IntToString(nSubRadSpell); if(nSpell == StringToInt(Get2DAString("spells", sSubRadSpell, nMSpell))) - nMmSpell = GetMemorizedSpellMetaMagic(oCaster, nClass, nLevel, nIndex); - nDSpell = GetMemorizedSpellIsDomainSpell(oCaster, nClass, nLevel, nIndex); - ai_Debug("pe_buffing", "421", "nMmSpell: " + IntToString(nMmSpell) + - " nMetamagic: " + IntToString(nMetamagic) + - " nDomain: " + IntToString(nDomain) + - " nDSpell: " + IntToString(nDSpell)); - if(nMmSpell == nMetamagic) { - nSpellMemorized = TRUE; - if(GetMemorizedSpellReady(oCaster, nClass, nLevel, nIndex)) + nMmSpell = GetMemorizedSpellMetaMagic(oCaster, nClass, nLevel, nIndex); + nDSpell = GetMemorizedSpellIsDomainSpell(oCaster, nClass, nLevel, nIndex); + //SendMessageToPC(oCaster, "pe_buffing, 433, nMmSpell: " + IntToString(nMmSpell) + + // " nMetamagic: " + IntToString(nMetamagic) + + // " nDomain: " + IntToString(nDomain) + + // " nDSpell: " + IntToString(nDSpell)); + if(nMmSpell == nMetamagic) { - if(nDSpell == nDomain) return TRUE; + nSpellMemorized = TRUE; + if(GetMemorizedSpellReady(oCaster, nClass, nLevel, nIndex)) + { + if((nDomain && nDSpell) || (!nDomain && !nDSpell)) return TRUE; + } } } } @@ -479,7 +510,6 @@ void PopupWidgetBuffGUIPanel(object oPC) SetLocalInt(oPC, AI_NO_NUI_SAVE, TRUE); DelayCommand(0.5f, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); // Row 1 (buttons)********************************************************** - json jRow = CreateButtonImage(JsonArray(), "ir_level1", "btn_one", 35.0f, 35.0f, 0.0); jRow = CreateButtonImage(jRow, "ir_level2", "btn_two", 35.0f, 35.0f, 0.0); jRow = CreateButtonImage(jRow, "ir_level3", "btn_three", 35.0f, 35.0f, 0.0); @@ -519,7 +549,6 @@ void PopupWidgetBuffGUIPanel(object oPC) if(bAIBuffWidgetLock) nToken = SetWindow (oPC, jLayout, "widgetbuffwin", "Fast Buff Widget", fX, fY, 160.0, 62.0, FALSE, FALSE, FALSE, TRUE, FALSE, "pe_buffing"); else nToken = SetWindow (oPC, jLayout, "widgetbuffwin", "Fast Buff Widget", fX, fY, 160.0, 95.0, FALSE, FALSE, FALSE, TRUE, TRUE, "pe_buffing"); // Set event watches for window inspector and save window location. - NuiSetBindWatch (oPC, nToken, "collapsed", TRUE); NuiSetBindWatch (oPC, nToken, "window_geometry", TRUE); // Set the buttons to show events. //NuiSetBind (oPC, nToken, "btn_one", JsonBool (TRUE)); diff --git a/src/module/nss/pe_crafting.nss b/src/module/nss/pe_crafting.nss index 388c86a..51d76e7 100644 --- a/src/module/nss/pe_crafting.nss +++ b/src/module/nss/pe_crafting.nss @@ -8,6 +8,11 @@ #include "nw_inc_gff" #include "0i_main" #include "0i_items" +// Banned list of BaseItemTypes use rows from baseitemtype.2da. +// Place each one between a : . Example ":21:28:" will not change belts and clubs. +// Best used for visual effect items like helms. +const string CRAFT_BANNED_BASEITEMTYPES = ":23:"; +//const string CRAFT_BANNED_BASEITEMTYPES = "::"; // Maximum model number for all items except weapons. const int CRAFT_MAX_MODEL_NUMBER = 999; @@ -20,7 +25,8 @@ struct stWeaponAppearance }; // Maximum model number for weapons. Note this will be the 100s and 10s places. // The color number uses the ones place. Thus 25 is actually 250. -const int CRAFT_MAX_WEAPON_MODEL_NUMBER = 99; +const int ALLOW_CRAFT_NAMES = TRUE; +const int CRAFT_MAX_WEAPON_MODEL_NUMBER = 99; const string CRAFT_JSON = "CRAFT_JSON"; const string CRAFT_ORIGINAL_ITEM = "CRAFT_ORIGINAL_ITEM"; const string CRAFT_COOL_DOWN = "CRAFT_COOL_DOWN"; @@ -46,6 +52,7 @@ const string CRAFT_LEFT_PART_COLOR = "CRAFT_LEFT_PART_COLOR"; const string CRAFT_ALL_COLOR = "CRAFT_ALL_COLOR"; const string CRAFT_RIGHT_PART_COLOR = "CRAFT_RIGHT_PART_COLOR"; const string CRAFT_TARGET = "CRAFT_TARGET"; +const string CRAFT_ITEM = "CRAFT_ITEM"; // Tag used in lighting effects. const string CRAFT_HIGHLIGHT = "CRAFT_HIGHLIGHT"; const string CRAFT_ULTRALIGHT = "CRAFT_ULTRALIGHT"; @@ -95,6 +102,11 @@ void CreateItemGUIPanel(object oPC, object oTarget); void CraftItemInfoEvents(object oPC, int nToken); // Creates the save/load menu for items. //void CreateDresserGUIPanel(object oPC, object oTarget); +json CreateItemCombo(object oPC, json jRow, string sComboBind); +json CreateModelCombo(object oPC, object oTarget, json jRow, string sComboBind); +void CreateCreatureCraftingGUIPanel(object oPC, object oTarget); +// See above for constant that can have base item types added to the list. +int IfOnBannedBaseItemTypeList(object oPC, object oItem); int GetColorIDChange(object oItem, int nType, int nIndex, int nChange) { @@ -123,14 +135,63 @@ void main() // Targeting code here. if(sTargetMode == "SELECT_TARGET") { - if(GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN || - ai_GetIsCharacter(oTarget)) + int nObjectType = GetObjectType(oTarget); + if(nObjectType == OBJECT_TYPE_CREATURE) { - SetLocalObject(oPC, CRAFT_TARGET, oTarget); - AttachCamera(oPC, oTarget); - ExecuteScript("pi_crafting", oPC); + if(oPC == oTarget || GetMaster(oTarget) == oPC || + ai_GetIsDungeonMaster(oPC)) + { + SetLocalObject(oPC, CRAFT_TARGET, oTarget); + AttachCamera(oPC, oTarget); + SetLocalObject(oPC, CRAFT_TARGET, oTarget); + CreateCreatureCraftingGUIPanel(oPC, oTarget); + } + else + { + ai_SendMessages(GetName(oTarget) + " is not the player or a henchmen! Other associates cannot use item crafting.", AI_COLOR_RED, oPC); + // Set this variable on the player so PEPS can run the targeting script for this plugin. + SetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT, "pe_crafting"); + // Set Targeting variables. + SetLocalString(oPC, AI_TARGET_MODE, "SELECT_TARGET"); + ai_SendMessages("Select your character, a henchman or an item possessed by one.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_ITEM , + MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + return; + } + } + else if(nObjectType == OBJECT_TYPE_ITEM) + { + if(!GetIdentified(oTarget) && !ai_GetIsDungeonMaster(oPC)) + { + ai_SendMessages("The item must be Identified!", AI_COLOR_RED, oPC); + // Set this variable on the player so PEPS can run the targeting script for this plugin. + SetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT, "pe_crafting"); + // Set Targeting variables. + SetLocalString(oPC, AI_TARGET_MODE, "SELECT_TARGET"); + ai_SendMessages("Select your character, a henchman or an item possessed by one of them.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_ITEM , + MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + return; + } + object oCreature = GetItemPossessor(oTarget, TRUE); + if(oCreature == oPC || GetMaster(oCreature) == oPC || ai_GetIsDungeonMaster(oPC)) + { + SetLocalObject(oPC, "CRAFT_INFO_ITEM", oTarget); + CreateItemGUIPanel(oPC, oTarget); + } + else + { + ai_SendMessages("Items must be possessed by the player or a henchmen!", AI_COLOR_RED, oPC); + // Set this variable on the player so PEPS can run the targeting script for this plugin. + SetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT, "pe_crafting"); + // Set Targeting variables. + SetLocalString(oPC, AI_TARGET_MODE, "SELECT_TARGET"); + ai_SendMessages("Select your character, a henchman or an item possessed by one of them.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_ITEM , + MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + return; + } } - else ai_SendMessages(GetName(oTarget) + " is not the player or a henchmen! Other associates cannot use item crafting.", AI_COLOR_RED, oPC); } DeleteLocalString(oPC, AI_TARGET_MODE); } @@ -196,9 +257,9 @@ void main() return; } // Delay crafting so it has time to equip and unequip as well as remove. - //if(GetLocalInt(oPC, CRAFT_COOL_DOWN)) return; - //SetLocalInt(oPC, CRAFT_COOL_DOWN, TRUE); - //DelayCommand(0.25f, DeleteLocalInt(oPC, CRAFT_COOL_DOWN)); + if(GetLocalInt(oPC, CRAFT_COOL_DOWN)) return; + SetLocalInt(oPC, CRAFT_COOL_DOWN, TRUE); + DelayCommand(0.25f, DeleteLocalInt(oPC, CRAFT_COOL_DOWN)); // They have selected a color. if(sElem == "color_pallet") { @@ -285,6 +346,7 @@ void main() if(nChange) nColorId = GetColorIDChange(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected, nChange); oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected, nColorId, TRUE); DestroyObject(oItem); + SetColorPalletPointer(oPC, nToken, oNewItem); } // Lock the new item so they can't change it on the character. LockItemInCraftingWindow(oPC, oNewItem, oTarget, nToken); @@ -318,7 +380,7 @@ void main() else jCraft = JsonObjectSet(jCraft, CRAFT_MODEL_SELECTION, JsonInt(0)); SetLocalJson(oPC, CRAFT_JSON, jCraft); NuiDestroy(oPC, nToken); - ExecuteScript("pi_crafting", oPC); + CreateCreatureCraftingGUIPanel(oPC, GetLocalObject(oPC, CRAFT_TARGET)); } // They have selected a part to change. else if(sElem == "model_combo_selected") @@ -413,7 +475,7 @@ void main() // Set Targeting variables. SetLocalString(oPC, AI_TARGET_MODE, "SELECT_TARGET"); NuiDestroy(oPC, nToken); - ai_SendMessages("Select either your charcter or a henchman to craft their equipment.", AI_COLOR_YELLOW, oPC); + ai_SendMessages("Select your charcter, a henchman or an item possessed by one.", AI_COLOR_YELLOW, oPC); DeleteLocalObject(oPC, CRAFT_ORIGINAL_ITEM); DeleteLocalObject(oPC, CRAFT_TARGET); DeleteLocalObject(oPC, "CRAFT_INFO_ITEM"); @@ -427,7 +489,8 @@ void main() RemoveTagedEffects(oTarget, CRAFT_HIGHLIGHT); DeleteLocalInt(oPC, CRAFT_HIGHLIGHT); } - EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE , MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_ITEM , + MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); } // Cancel any changes made to the selected item. else if(sElem == "btn_cancel") @@ -438,7 +501,7 @@ void main() CancelCraftedItem(oPC, oTarget); ClearItemInCraftingWindow(oPC, oItem, nToken); DelayCommand(0.5, NuiDestroy(oPC, nToken)); - DelayCommand(0.5, ExecuteScript("pi_crafting", oPC)); + DelayCommand(0.5, CreateCreatureCraftingGUIPanel(oPC, GetLocalObject(oPC, CRAFT_TARGET))); } // If the button is on Exit not Cancel then exit. else @@ -643,6 +706,7 @@ void main() int nSelected = StringToInt(GetStringRight(sElem, 1)); SetMaterialButtons(oPC, nToken, nSelected); jCraft = JsonObjectSet(jCraft, CRAFT_MATERIAL_SELECTION, JsonInt(nSelected)); + SetLocalJson(oPC, CRAFT_JSON, jCraft); // Change the pallet for the correct material. string sColorPallet; if(nSelected < 4) @@ -1181,6 +1245,8 @@ object ChangeItemsAppearance(object oPC, object oTarget, int nToken, object oIte // " nModelSelected: " + IntToString(nModelSelected)); } // Change the model. + //WriteTimestampedLogEntry("pe_crafting, 1241, " + GetName(oItem) + " nModelSelected: " + + // IntToString(nModelSelected) + " nModelNumber: " + IntToString(nModelNumber)); oNewItem = CopyItemAndModify (oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nModelSelected, nModelNumber, TRUE); DestroyObject (oItem); AssignCommand (oTarget, ActionEquipItem (oNewItem, INVENTORY_SLOT_CHEST)); @@ -1213,6 +1279,8 @@ object ChangeItemsAppearance(object oPC, object oTarget, int nToken, object oIte // Note: Right Thigh and Left Thigh are backwards so this fixes that! if (nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected--; else nModelSelected++; + //WriteTimestampedLogEntry("pe_crafting, 1275, " + GetName(oItem) + " nModelSelected: " + + // IntToString(nModelSelected) + " nModelNumber: " + IntToString(nModelNumber)); oItem = CopyItemAndModify(oNewItem, ITEM_APPR_TYPE_ARMOR_MODEL, nModelSelected, nModelNumber, TRUE); DestroyObject(oNewItem); AssignCommand(oTarget, ActionEquipItem(oItem, INVENTORY_SLOT_CHEST)); @@ -1431,17 +1499,18 @@ void CancelCraftedItem(object oPC, object oTarget) { json jCraft = GetLocalJson(oPC, CRAFT_JSON); int nItemSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); - object oItem = GetSelectedItem(oTarget, nItemSelected); + object oItem = GetLocalObject(oPC, CRAFT_ITEM); object oOriginalItem = GetLocalObject(oPC, CRAFT_ORIGINAL_ITEM); if(oOriginalItem != OBJECT_INVALID) { - DestroyObject(oItem); int nSlot = GetItemSelectedEquipSlot(nItemSelected); // Give item Backup to Player oOriginalItem = CopyItem(oOriginalItem, oTarget, TRUE); DelayCommand(0.2f, AssignCommand (oTarget, ActionEquipItem(oOriginalItem, nSlot))); DeleteLocalObject(oPC, CRAFT_ORIGINAL_ITEM); } + DestroyObject(oItem); + DeleteLocalObject(oPC, CRAFT_ITEM); } // Gets the colorId from a image of the color pallet. // Thanks Zunath for the base code. @@ -1499,6 +1568,7 @@ void LockItemInCraftingWindow(object oPC, object oItem, object oTarget, int nTok // Make sure the item information window is closed. nToken = NuiFindWindow(oPC, "craft_item_nui"); if(nToken) NuiDestroy(oPC, nToken); + SetLocalObject(oPC, CRAFT_ITEM, oItem); } void ClearItemInCraftingWindow(object oPC, object oItem, int nToken) { @@ -1527,6 +1597,7 @@ void SaveCraftedItem(object oPC, object oTarget, int nToken) } int CanCraftItem(object oPC, object oItem, int nToken, int bPasteCheck = FALSE) { + if(IfOnBannedBaseItemTypeList(oPC, oItem)) return FALSE; // Plot items cannot be changed. if(GetPlotFlag(oItem)) { @@ -1890,55 +1961,75 @@ void CreateItemGUIPanel(object oPC, object oItem) jRow = CreateTextEditBox (jRow, "name_placeholder", "txt_item_name", 60, FALSE, 325.0f, 20.0f); // Add row to the column. json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); - // Row 2 (Tag)************************************************************** 101 - jRow = CreateLabel(JsonArray(), "Tag:", "lbl_tag_title", 50.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateTextEditBox(jRow, "name_placeholder", "txt_item_tag", 60, FALSE, 325.0f, 20.0f); - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 2 (ResRef)*********************************************************** 129 - jRow = CreateLabel(JsonArray(), "ResRef:", "lbl_resref_title", 50.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateTextEditBox(jRow, "name_placeholder", "txt_item_resref", 60, FALSE, 325.0f, 20.0f); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + float fHeight = 113.0; + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC)) + { + // Row 2 (Tag)************************************************************** 101 + jRow = CreateLabel(JsonArray(), "Tag:", "lbl_tag_title", 50.0f, 20.0f, NUI_HALIGN_LEFT); + jRow = CreateTextEditBox(jRow, "name_placeholder", "txt_item_tag", 60, FALSE, 325.0f, 20.0f); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 2 (ResRef)*********************************************************** 129 + jRow = CreateLabel(JsonArray(), "ResRef:", "lbl_resref_title", 50.0f, 20.0f, NUI_HALIGN_LEFT); + jRow = CreateTextEditBox(jRow, "name_placeholder", "txt_item_resref", 60, FALSE, 325.0f, 20.0f); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 56.0; + } // Row 3 (Base Item/Weight)************************************************* 157 - jRow = CreateLabel(JsonArray(), "Base Item: ", "lbl_baseitem_title", 75.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateLabel(jRow, "", "lbl_baseitem", 145.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateLabel(jRow, "Weight: ", "lbl_weight_title", 55.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateLabel(jRow, "", "lbl_weight", 65.0f, 20.0f, NUI_HALIGN_LEFT); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 4 (Gold Value)******************************************************* 185 - jRow = CreateLabel(JsonArray(), "Gold Value: ", "lbl_gold_title", 85.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateLabel(jRow, "", "lbl_gold_value", 135.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateLabel(jRow, "Minimum Level: ", "lbl_min_lvl_title", 110.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateLabel(jRow, "", "lbl_min_lvl", 20.0f, 20.0f, NUI_HALIGN_LEFT); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 5 (Plot/Stolen)****************************************************** 213 - jRow = CreateCheckBox(JsonArray(), " Plot", "chbx_plot", 110.0, 20.0f, "chbx_plot_tooltip"); - jRow = CreateCheckBox(jRow, " Stolen", "chbx_stolen", 110.0, 20.0f, "chbx_stolen_tooltip"); - jRow = CreateCheckBox(jRow, " Cursed", "chbx_cursed", 110.0, 20.0f, "chbx_cursed_tooltip"); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 6 (Identified/Droppable)********************************************* 269 - jRow = CreateCheckBox(JsonArray(), " Identified", "chbx_identified", 110.0, 25.0f, "chbx_identified_tooltip"); - jRow = CreateCheckBox(jRow, " Droppable", "chbx_droppable", 110.0, 25.0f, "chbx_droppable_tooltip"); - jRow = CreateButton(jRow, "Save as UTI", "btn_save_uti", 110.0, 25.0, -1.0, "btn_save_uti_tooltip"); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 9 (Stack/Variables/Destroy/Charges)********************************** 307 - jRow = CreateTextEditBox(JsonArray(), "name_placeholder", "txt_stack", 4, FALSE, 35.0f, 25.0f); - jRow = CreateLabel(jRow, " Stack", "lbl_stack_title", 72.0f, 20.0f, NUI_HALIGN_LEFT); - jRow = CreateTextEditBox(jRow, "name_placeholder", "txt_charges", 4, FALSE, 40.0f, 25.0f); - jRow = CreateLabel(jRow, " Charges", "lbl_charges_title", 68.0f, 25.0f, NUI_HALIGN_LEFT); - jRow = CreateButtonSelect(jRow, "Destroy", "btn_destroy", 110.0, 25.0, "btn_destroy_tooltip"); + jRow = CreateLabel(JsonArray(), "Base Item: ", "lbl_baseitem_title", 67.0f, 20.0f, NUI_HALIGN_LEFT); + jRow = CreateLabel(jRow, "", "lbl_baseitem", 120.0f, 20.0f, NUI_HALIGN_LEFT); + jRow = CreateLabel(jRow, "Weight: ", "lbl_weight_title", 48.0f, 20.0f, NUI_HALIGN_LEFT); + jRow = CreateLabel(jRow, "", "lbl_weight", 30.0f, 20.0f, NUI_HALIGN_LEFT); + jRow = CreateButton(jRow, "Select Target", "btn_select_target", 100.0f, 20.0f); // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC)) + { + // Row 4 (Gold Value)******************************************************* 185 + jRow = CreateLabel(JsonArray(), "Gold Value: ", "lbl_gold_title", 85.0f, 25.0f, NUI_HALIGN_LEFT); + jRow = CreateLabel(jRow, "", "lbl_gold_value", 135.0f, 25.0f, NUI_HALIGN_LEFT); + jRow = CreateLabel(jRow, "Minimum Level: ", "lbl_min_lvl_title", 110.0f, 25.0f, NUI_HALIGN_LEFT); + jRow = CreateLabel(jRow, "", "lbl_min_lvl", 20.0f, 25.0f, NUI_HALIGN_LEFT); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 5 (Plot/Stolen)****************************************************** 213 + jRow = CreateCheckBox(JsonArray(), " Plot", "chbx_plot", 110.0, 25.0f, "chbx_plot_tooltip"); + jRow = CreateCheckBox(jRow, " Stolen", "chbx_stolen", 110.0, 25.0f, "chbx_stolen_tooltip"); + jRow = CreateCheckBox(jRow, " Cursed", "chbx_cursed", 110.0, 25.0f, "chbx_cursed_tooltip"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 6 (Identified/Droppable)********************************************* 269 + jRow = CreateCheckBox(JsonArray(), " Identified", "chbx_identified", 110.0, 25.0f, "chbx_identified_tooltip"); + jRow = CreateCheckBox(jRow, " Droppable", "chbx_droppable", 110.0, 25.0f, "chbx_droppable_tooltip"); + jRow = CreateButton(jRow, "Save as UTI", "btn_save_uti", 110.0, 25.0, -1.0, "btn_save_uti_tooltip"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 9 (Stack/Variables/Destroy/Charges)********************************** 307 + jRow = CreateTextEditBox(JsonArray(), "name_placeholder", "txt_stack", 4, FALSE, 35.0f, 25.0f); + jRow = CreateLabel(jRow, " Stack", "lbl_stack_title", 72.0f, 20.0f, NUI_HALIGN_LEFT); + jRow = CreateTextEditBox(jRow, "name_placeholder", "txt_charges", 4, FALSE, 40.0f, 25.0f); + jRow = CreateLabel(jRow, " Charges", "lbl_charges_title", 68.0f, 25.0f, NUI_HALIGN_LEFT); + jRow = CreateButtonSelect(jRow, "Destroy", "btn_destroy", 110.0, 25.0, "btn_destroy_tooltip"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 132.0; + } // Row 11 (Description)***************************************************** 558 jRow = CreateTextEditBox(JsonArray(), "desc_placeholder", "txt_desc", 1000, TRUE, 375.0, 243.0, "txt_desc_tooltip"); // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - float fHeight = 566.0; - // Row 12 (Item Base Description)* ***************************************** 158 + fHeight += 251.0; + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC) || ALLOW_CRAFT_NAMES) + { + // Row 12 (Description Save Button)***************************************** 558 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Save Description", "btn_save_desc", 150.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } fHeight += 28.0; + // Row 13 (Item Base Description)* ***************************************** 158 int nBaseItemType = GetBaseItemType(oItem); float fWeight; string sBaseItemDesc; @@ -1968,65 +2059,80 @@ void CreateItemGUIPanel(object oPC, object oItem) int nToken = SetWindow (oPC, jLayout, "craft_item_nui", sName + "'s item menu", -1.0, -1.0, 400.0, fHeight, FALSE, FALSE, TRUE, FALSE, TRUE, "pe_crafting"); // Set the buttons to show events to 0e_window. - NuiSetBind(oPC, nToken, "txt_item_name_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "txt_item_name", JsonString(GetName(oItem))); - NuiSetBindWatch(oPC, nToken, "txt_item_name", TRUE); - NuiSetBind(oPC, nToken, "txt_item_tag_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_item_tag", JsonString(GetTag(oItem))); - NuiSetBindWatch(oPC, nToken, "txt_item_tag", TRUE); - NuiSetBind(oPC, nToken, "txt_item_resref_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_item_resref", JsonString(GetResRef(oItem))); - NuiSetBindWatch(oPC, nToken, "txt_item_resref", TRUE); + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC) || ALLOW_CRAFT_NAMES) + { + NuiSetBind(oPC, nToken, "txt_item_name_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "txt_item_name", TRUE); + } + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC)) + { + NuiSetBind(oPC, nToken, "txt_item_tag_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_item_tag", JsonString(GetTag(oItem))); + NuiSetBindWatch(oPC, nToken, "txt_item_tag", TRUE); + NuiSetBind(oPC, nToken, "txt_item_resref_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_item_resref", JsonString(GetResRef(oItem))); + NuiSetBindWatch(oPC, nToken, "txt_item_resref", TRUE); + } string sValue = GetStringByStrRef(StringToInt(Get2DAString("baseitems", "Name", nBaseItemType))); NuiSetBind(oPC, nToken, "lbl_baseitem_label", JsonString(sValue)); sValue = FloatToString(fWeight * 0.1f, 0, 1); NuiSetBind(oPC, nToken, "lbl_weight_label", JsonString(sValue)); - int nValue = GetGoldPieceValue(oItem); - NuiSetBind (oPC, nToken, "lbl_gold_value_label", JsonString(IntToString(nValue))); - sValue = IntToString (ai_GetMinimumEquipLevel(oItem)); - NuiSetBind(oPC, nToken, "lbl_min_lvl_label", JsonString (sValue)); - nValue = GetPlotFlag (oItem); - NuiSetBind(oPC, nToken, "chbx_plot_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "chbx_plot_check", JsonBool(nValue)); - NuiSetBindWatch(oPC, nToken, "chbx_plot_check", TRUE); - NuiSetBind(oPC, nToken, "chbx_plot_tooltip", JsonString (" Plot items cannot be sold or destroyed.")); - nValue = GetStolenFlag(oItem); - NuiSetBind(oPC, nToken, "chbx_stolen_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "chbx_stolen_check", JsonBool(nValue)); - NuiSetBindWatch (oPC, nToken, "chbx_stolen_check", TRUE); - NuiSetBind(oPC, nToken, "chbx_stolen_tooltip", JsonString (" Stolen items cannot be sold to some stores.")); - nValue = GetItemCursedFlag(oItem); - NuiSetBind(oPC, nToken, "chbx_cursed_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "chbx_cursed_check", JsonBool(nValue)); - NuiSetBindWatch (oPC, nToken, "chbx_cursed_check", TRUE); - NuiSetBind(oPC, nToken, "chbx_cursed_tooltip", JsonString (" Cursed items cannot be dropped or sold.")); - nValue = GetIdentified (oItem); - NuiSetBind(oPC, nToken, "chbx_identified_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "chbx_identified_check", JsonBool(nValue)); - NuiSetBindWatch(oPC, nToken, "chbx_identified_check", TRUE); - NuiSetBind(oPC, nToken, "chbx_identified_tooltip", JsonString (" Close inventory and open again to refresh identified state.")); - nValue = GetDroppableFlag(oItem); - NuiSetBind(oPC, nToken, "chbx_droppable_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "chbx_droppable_check", JsonBool(nValue)); - NuiSetBindWatch(oPC, nToken, "chbx_droppable_check", TRUE); - NuiSetBind(oPC, nToken, "chbx_droppable_tooltip", JsonString (" Droppable items only work on death of an NPC.")); - NuiSetBind(oPC, nToken, "btn_save_uti_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_save_uti_tooltip", JsonString (" Saves item to a UTI file. Update will be used in the game.")); - nValue = GetItemStackSize (oItem); - NuiSetBind(oPC, nToken, "txt_stack_event", JsonBool(TRUE)); - NuiSetBind (oPC, nToken, "txt_stack", JsonString(IntToString (nValue))); - NuiSetBindWatch (oPC, nToken, "txt_stack", TRUE); - nValue = GetItemCharges (oItem); - NuiSetBind(oPC, nToken, "txt_charges_event", JsonBool(TRUE)); - NuiSetBind (oPC, nToken, "txt_charges", JsonString(IntToString (nValue))); - NuiSetBindWatch (oPC, nToken, "txt_charges", TRUE); - NuiSetBind(oPC, nToken, "btn_destroy_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_destroy_tooltip", JsonString(" Destroys the item permanently! Must click twice to destroy the item.")); + NuiSetBind(oPC, nToken, "btn_select_target_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_select_target_tooltip", JsonString(" Select another Item")); + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC)) + { + int nValue = GetGoldPieceValue(oItem); + NuiSetBind (oPC, nToken, "lbl_gold_value_label", JsonString(IntToString(nValue))); + sValue = IntToString (ai_GetMinimumEquipLevel(oItem)); + NuiSetBind(oPC, nToken, "lbl_min_lvl_label", JsonString (sValue)); + nValue = GetPlotFlag (oItem); + NuiSetBind(oPC, nToken, "chbx_plot_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_plot_check", JsonBool(nValue)); + NuiSetBindWatch(oPC, nToken, "chbx_plot_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_plot_tooltip", JsonString (" Plot items cannot be sold or destroyed.")); + nValue = GetStolenFlag(oItem); + NuiSetBind(oPC, nToken, "chbx_stolen_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_stolen_check", JsonBool(nValue)); + NuiSetBindWatch (oPC, nToken, "chbx_stolen_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_stolen_tooltip", JsonString (" Stolen items cannot be sold to some stores.")); + nValue = GetItemCursedFlag(oItem); + NuiSetBind(oPC, nToken, "chbx_cursed_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_cursed_check", JsonBool(nValue)); + NuiSetBindWatch (oPC, nToken, "chbx_cursed_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_cursed_tooltip", JsonString (" Cursed items cannot be dropped or sold.")); + nValue = GetIdentified (oItem); + NuiSetBind(oPC, nToken, "chbx_identified_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_identified_check", JsonBool(nValue)); + NuiSetBindWatch(oPC, nToken, "chbx_identified_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_identified_tooltip", JsonString (" Close inventory and open again to refresh identified state.")); + nValue = GetDroppableFlag(oItem); + NuiSetBind(oPC, nToken, "chbx_droppable_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_droppable_check", JsonBool(nValue)); + NuiSetBindWatch(oPC, nToken, "chbx_droppable_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_droppable_tooltip", JsonString (" Droppable items only work on death of an NPC.")); + NuiSetBind(oPC, nToken, "btn_save_uti_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_save_uti_tooltip", JsonString (" Saves item to a UTI file. Update will be used in the game.")); + nValue = GetItemStackSize (oItem); + NuiSetBind(oPC, nToken, "txt_stack_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "txt_stack", JsonString(IntToString (nValue))); + NuiSetBindWatch (oPC, nToken, "txt_stack", TRUE); + nValue = GetItemCharges (oItem); + NuiSetBind(oPC, nToken, "txt_charges_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "txt_charges", JsonString(IntToString (nValue))); + NuiSetBindWatch (oPC, nToken, "txt_charges", TRUE); + NuiSetBind(oPC, nToken, "btn_destroy_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_destroy_tooltip", JsonString(" Destroys the item permanently! Must click twice to destroy the item.")); + } // Description - NuiSetBind(oPC, nToken, "txt_desc_event", JsonBool(TRUE)); - NuiSetBindWatch(oPC, nToken, "txt_desc", TRUE); - NuiSetBind(oPC, nToken, "txt_desc_tooltip", JsonString (" Color codes can be used!")); NuiSetBind(oPC, nToken, "txt_desc", JsonString(GetDescription(oItem))); + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC) || ALLOW_CRAFT_NAMES) + { + NuiSetBind(oPC, nToken, "txt_desc_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "txt_desc", TRUE); + NuiSetBind(oPC, nToken, "txt_desc_tooltip", JsonString (" Color codes can be used!")); + NuiSetBind(oPC, nToken, "btn_save_desc_event", JsonBool(TRUE)); + } // Base Item Description NuiSetBind(oPC, nToken, "txt_base_desc_event", JsonBool(TRUE)); //NuiSetBind(oPC, nToken, "txt_desc_tooltip", JsonString ("Color codes can be used!")); @@ -2048,6 +2154,17 @@ void CraftItemInfoEvents(object oPC, int nToken) object oItem = GetLocalObject(oPC, "CRAFT_INFO_ITEM"); if(sEvent == "click") { + if(sElem == "btn_select_target") + { + // Set this variable on the player so PEPS can run the targeting script for this plugin. + SetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT, "pe_crafting"); + // Set Targeting variables. + SetLocalString(oPC, AI_TARGET_MODE, "SELECT_TARGET"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select your charcter, a henchman or an item possessed by one.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_ITEM , + MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } if(sElem == "btn_destroy") { if(!JsonGetInt(NuiGetBind(oPC, nToken, "btn_destroy"))) @@ -2082,6 +2199,11 @@ void CraftItemInfoEvents(object oPC, int nToken) ai_SendMessages("This temp directory will be removed when the game is left.", AI_COLOR_GREEN, oPC); } } + if(sElem == "btn_save_desc") + { + string sDescription = JsonGetString(NuiGetBind(oPC, nToken, "txt_desc")); + SetDescription(oItem, sDescription); + } } if(sEvent == "watch") { @@ -2149,5 +2271,614 @@ void CraftItemInfoEvents(object oPC, int nToken) } /*void CreateDresserGUIPanel(object oPC, object oTarget) { +} */ +json CreateItemCombo(object oPC, json jRow, string sComboBind) +{ + int nCnt; + // Create the list. + json jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Armor", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Cloak", 1)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Headgear", 2)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Right hand", 3)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Left hand", 4)); + return CreateCombo(jRow, jCombo, sComboBind, 128.0, 40.0); +} +json CreateModelCombo(object oPC, object oTarget, json jRow, string sComboBind) +{ + float fFacing = GetFacing(oTarget); + json jCombo, jCraft = GetLocalJson(oPC, CRAFT_JSON); + int nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + // Create the list. + // Armor. + if(nSelected == 0) + { + fFacing += 180.0f; + if (fFacing > 359.0) fFacing -=359.0; + AssignCommand(oPC, SetCameraFacing(fFacing, 4.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); + jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Neck", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Shoulder", 1)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Bicep", 2)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Forearm", 3)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Hand", 4)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Torso", 5)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Belt", 6)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Pelvis", 7)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Thigh", 8)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Shin", 9)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Foot", 10)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Robe", 11)); + } + // Cloak. + else if(nSelected == 1) + { + if(fFacing > 359.0) fFacing -=359.0; + AssignCommand (oPC, SetCameraFacing(fFacing, 4.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); + jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Cloak", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Invisible", 1)); + } + // Headgear. + else if (nSelected == 2) + { + fFacing += 180.0f; + if(fFacing > 359.0) fFacing -=359.0; + AssignCommand(oPC, SetCameraFacing(fFacing, 2.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); + jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Headgear", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Invisible", 1)); + } + // Weapon. + else if (nSelected == 3) + { + // If they are changing a bow then face the opposite side. + object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); + int nBaseItemType = GetBaseItemType(oItem); + if(nBaseItemType == BASE_ITEM_LONGBOW || nBaseItemType == BASE_ITEM_SHORTBOW) fFacing -= 90.00; + // This will make the camera face a melee weapon. + else fFacing += 90.0; + if(fFacing > 359.0) fFacing -=359.0; + AssignCommand(oPC, SetCameraFacing(fFacing, 3.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); + jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Weapon", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Acidic", 1)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Frost", 2)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Electric", 3)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Unholy", 4)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Flaming", 5)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Holy", 6)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Sonic", 7)); +} + // Weapon/Shield. + else if(nSelected == 4) + { + fFacing += 270.0f; + if(fFacing > 359.0) fFacing -=359.0; + AssignCommand(oPC, SetCameraFacing(fFacing, 3.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); + object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); + if(ai_GetIsShield(oItem)) + { + jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Shield", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Invisible", 1)); + } + else + { + jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Weapon", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Acidic", 1)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Frost", 2)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Electric", 3)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Unholy", 4)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Flaming", 5)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Holy", 6)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Sonic", 7)); + } + } + return CreateCombo(jRow, jCombo, sComboBind, 128.0, 40.0); +} +void CreateCreatureCraftingGUIPanel(object oPC, object oTarget) +{ + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + if(JsonGetType(jCraft) == JSON_TYPE_NULL) jCraft = JsonObject(); + // Row 1 (Object Name)****************************************************** 508 / 83 + json jRow = CreateTextEditBox(JsonArray(), "plc_hold_bind", "txt_item_name", 50, FALSE, 486.0f, 30.0f); // 419 + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 (Object Name)****************************************************** 508 / 121 + jRow = JsonArray(); + jRow = CreateButton(jRow, "Information", "btn_info", 160.0f, 30.0f, -1.0, "btn_info_tooltip"); + jRow = CreateButton(jRow, "Wardrobe", "btn_wardrobe", 158.0f, 30.0f, -1.0, "btn_wardrobe_tooltip"); + jRow = CreateButtonSelect(jRow, "Add Light", "btn_highlight", 160.0f, 30.0f, "btn_highlight_tooltip"); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 (Object Name)****************************************************** 508 / 159 + jRow = CreateButton(JsonArray(), "Save", "btn_save", 160.0f, 30.0f, -1.0, "btn_save_tooltip"); + jRow = CreateButton(jRow, "Select Target", "btn_select_target", 158.0f, 30.0f, -1.0, "btn_select_target_tooltip"); + jRow = CreateButton(jRow, "", "btn_cancel", 160.0f, 30.0f, -1.0, "btn_cancel_tooltip"); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 (labels)*********************************************************** 508 / 177 + jRow = CreateLabel(JsonArray(), "Model", "module_title", 143.0f, 10.0f); + jRow = CreateLabel(jRow, "Color", "color_title", 339.0f, 10.0f); + jRow = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 5 (groups) + // Row 51 (title)*********************************************************** 508 / 195 / 18 + json jGroupRow = CreateLabel(JsonArray(), "Item", "item__cmb_title", 128.0f, 10.0f); + json jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + // Row 52 (combo)*********************************************************** 508 / 233 / 56 + jGroupRow = CreateItemCombo(oPC, JsonArray(), "item_combo"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 53 (title)*********************************************************** 508 / 251 / 74 + jGroupRow = CreateLabel(JsonArray(), "Model", "model_cmb_title",128.0f, 10.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 54 (combo)*********************************************************** 508 / 289 / 112 + jGroupRow = CreateModelCombo(oPC, oTarget, JsonArray(), "model_combo"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 55 (title)*********************************************************** 508 / 307 / 120 + jGroupRow = CreateLabel(JsonArray(), "", "top_title",128.0f, 10.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 56 (top)************************************************************* 508 / 355 / 168 + jGroupRow = CreateButtonImage(JsonArray(), "nui_shld_left", "btn_prev_t", 40.0f, 40.0f); + // Removed TextEditBox for mobile + jGroupRow = CreateTextEditBox(jGroupRow, "place_holder", "txt_model_number_t", 3, FALSE, 40.0, 40.0); + //CreateLabel(jGroupRow, "", "txt_model_number_t", 40.0, 40.0); + jGroupRow = CreateButtonImage(jGroupRow, "nui_shld_right", "btn_next_t", 40.0f, 40.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 57 (title)*********************************************************** 508 / 373 / 186 + jGroupRow = CreateLabel(JsonArray(), "", "middle_title",128.0f, 10.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 58 (middle)********************************************************** 508 / 421 /234 + jGroupRow = CreateButtonImage(JsonArray(), "nui_shld_left", "btn_prev_m", 40.0f, 40.0f); + // Removed TextEditBox for mobile + jGroupRow = CreateTextEditBox(jGroupRow, "place_holder", "txt_model_number_m", 3, FALSE, 40.0, 40.0); + //CreateLabel(jGroupRow, "", "txt_model_number_m", 40.0, 40.0); + jGroupRow = CreateButtonImage(jGroupRow, "nui_shld_right", "btn_next_m", 40.0f, 40.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 59 (title)*********************************************************** 508 / 439 / 252 + jGroupRow = CreateLabel(JsonArray(), "", "bottom_title",128.0f, 10.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 510 (bottom)********************************************************* 508 / 487 /300 + jGroupRow = CreateButtonImage(JsonArray(), "nui_shld_left", "btn_prev_b", 40.0f, 40.0f); + // Removed TextEditBox for mobile + jGroupRow = CreateTextEditBox(jGroupRow, "place_holder", "txt_model_number_b", 3, FALSE, 40.0, 40.0); + //CreateLabel(jGroupRow, "", "txt_model_number_b", 40.0, 40.0); + jGroupRow = CreateButtonImage(jGroupRow, "nui_shld_right", "btn_next_b", 40.0f, 40.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 511 (blank spacer) + jGroupRow = CreateLabel(JsonArray(), "", "blank_space",128.0f, 20.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 512 (light)********************************************************** 508 / 487 /300 + jGroupRow = CreateButtonSelect(JsonArray(), "Randomize", "btn_randomize", 128.0f, 30.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupCol = JsonArrayInsert(jGroupCol, NuiSpacer()); + jRow = JsonArrayInsert(JsonArray(), NuiHeight(NuiWidth(NuiGroup(NuiCol(jGroupCol)), 143.0), 442.0)); + // Make the Color Group. + // Row 550 (groups)********************************************************* 508 / 361 / 184 + json jImage = NuiEnabled(NuiId(NuiImage(NuiBind("color_pallet_image"), JsonInt(0), JsonInt(0), JsonInt(1)), "color_pallet"), NuiBind("color_pallet_event")); + jImage = NuiWidth(jImage, 320.0); // 256 + 64 + jImage = NuiHeight(jImage, 220.0); // 176 + 44 + jImage = NuiTooltip(jImage, NuiBind("color_pallet_tooltip")); + json jIndicator = JsonArrayInsert(JsonArray(), NuiDrawListRect(JsonBool(TRUE), NuiColor(255,0,0), JsonBool(FALSE), JsonFloat(2.0), NuiBind("color_pallet_pointer"))); + jImage = NuiDrawList(jImage, JsonBool(FALSE), jIndicator); + jGroupRow = JsonArrayInsert(JsonArray(), jImage); + jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + // Row 551 (groups)********************************************************* 508 / 379 /202 + jGroupRow = CreateLabel(JsonArray(), "Part To Color", "lbl_color_parts", 320.0f, 10.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 552 (groups)********************************************************* 508 / 417 /240 + jGroupRow = CreateButtonSelect(JsonArray(), "Right", "btn_right_part_color", 98.0, 30.0, "btn_right_part_color_tooltip"); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButtonSelect(jGroupRow, "All", "btn_all_color", 98.0, 30.0, "btn_all_color_tooltip"); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButtonSelect(jGroupRow, "Left", "btn_left_part_color", 98.0, 30.0, "btn_left_part_color_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 553 (groups)********************************************************* 508 / 435 / 258 + jGroupRow = CreateLabel(JsonArray(), "Part Color To Reset", "lbl_reset_parts", 320.0f, 10.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 554 (groups)********************************************************* 508 / 473 /296 + jGroupRow = CreateButton(JsonArray(), "Right", "btn_right_part_reset", 98.0, 30.0, -1.0, "btn_right_part_reset_tooltip"); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButton(jGroupRow, "All", "btn_all_reset", 50.0, 30.0, -1.0, "btn_all_reset_tooltip"); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButton(jGroupRow, "Left", "btn_left_part_reset", 98.0, 30.0, -1.0, "btn_left_part_reset_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 555 (groups)********************************************************* 508 / 491 / 314 + jGroupRow = CreateLabel(JsonArray(), "Material to Color", "lbl_material_color", 320.0f, 10.0f); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 556 (groups)********************************************************* 508 / 529 /352 + jGroupRow = CreateButtonSelect(JsonArray(), "Leather 1", "btn_material_0", 98.0, 30.0); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButtonSelect(jGroupRow, "Cloth 1", "btn_material_2", 98.0, 30.0); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButtonSelect(jGroupRow, "Metal 1", "btn_material_4", 98.0, 30.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Row 557 (groups)********************************************************* 508 / 567 / 390 + jGroupRow = CreateButtonSelect(JsonArray(), "Leather 2", "btn_material_1", 98.0, 30.0); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButtonSelect(jGroupRow, "Cloth 2", "btn_material_3", 98.0, 30.0); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButtonSelect(jGroupRow, "Metal 2", "btn_material_5", 98.0, 30.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupCol = JsonArrayInsert(jGroupCol, NuiSpacer()); + jRow = JsonArrayInsert(jRow, NuiHeight(NuiWidth(NuiGroup(NuiCol(jGroupCol)), 339.0), 442.0)); // 275 398 + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + json jLayout = NuiCol(jCol); + // Get the window location to restore it from the database. + json jGeometry = JsonObjectGet(jCraft, "CRAFT_MENU"); + float fX = JsonGetFloat(JsonObjectGet(jGeometry, "x")); + float fY = JsonGetFloat(JsonObjectGet(jGeometry, "y")); + string sPCWindow; + int nToken = SetWindow(oPC, jLayout, "crafting_nui", "Crafting", + fX, fY, 508.0, 700.0, FALSE, FALSE, FALSE, FALSE, TRUE, "pe_crafting"); // 444 645 + // Set all binds, events, and watches. + NuiSetBindWatch (oPC, nToken, "window_geometry", TRUE); + int nItem = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + object oItem = GetSelectedItem(oTarget, nItem); + // Row 1 + NuiSetBind(oPC, nToken, "txt_item_name", JsonString(GetName(oItem))); + if(!ai_GetIsServer() || ai_GetIsDungeonMaster(oPC) || ALLOW_CRAFT_NAMES) + { + NuiSetBind(oPC, nToken, "txt_item_name_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "txt_item_name", TRUE); + } + // Row 2 + NuiSetBind(oPC, nToken, "btn_info_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_info_tooltip", JsonString(" Look at and change item information")); + NuiSetBind(oPC, nToken, "btn_wardrobe_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_wardrobe_tooltip", JsonString(" Use your wardrobe to save/load item appearances")); + int nLight = GetLocalInt(oPC, CRAFT_HIGHLIGHT) + GetLocalInt(oPC, CRAFT_ULTRALIGHT); + NuiSetBind(oPC, nToken, "btn_highlight", JsonBool(nLight)); + NuiSetBind(oPC, nToken, "btn_highlight_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_highlight_tooltip", JsonString(" Left click for White light, Right click for Ultravision")); + // Row 3 + NuiSetBind(oPC, nToken, "btn_save_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_save_tooltip", JsonString(" Save current changes")); + NuiSetBind(oPC, nToken, "btn_select_target_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_select_target_tooltip", JsonString(" Select another party member or Item")); + NuiSetBind(oPC, nToken, "btn_cancel_label", JsonString("Exit")); + NuiSetBind(oPC, nToken, "btn_cancel_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cancel_tooltip", JsonString(" Exit the crafting menu")); + // Row 4 Labels. + // Row 5 Groups. + // Row 51 title. + // Row 52 + NuiSetBind(oPC, nToken, "item_combo_selected", JsonInt(nItem)); + NuiSetBind(oPC, nToken, "item_combo_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "item_combo_selected", TRUE); + // Row 53 title. + // Row 54 + int nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MODEL_SELECTION)); + if(nItem == 1 || nItem == 2 || nItem == 4) + { + if(GetHiddenWhenEquipped(oItem)) nSelected = 1; + else nSelected = 0; + } + NuiSetBind(oPC, nToken, "model_combo_selected", JsonInt (nSelected)); + NuiSetBind(oPC, nToken, "model_combo_event", JsonBool (TRUE)); + NuiSetBindWatch(oPC, nToken, "model_combo_selected", TRUE); + // Row 55, 56, 57 titles + // Row 58 top, 59 middle, 510 bottom + string sModelTop, sModelMiddle, sModelBottom; + // Model Group + if(ai_GetIsWeapon(oItem)) + { + int nModel = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, 0); + int nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, 0); + int nModelNumber = (nModel * 10) + nColor; + sModelTop = IntToString(nModelNumber); + nModel = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, 1); + nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, 1); + nModelNumber = (nModel * 10) + nColor; + sModelMiddle = IntToString(nModelNumber); + nModel = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, 2); + nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, 2); + nModelNumber = (nModel * 10) + nColor; + sModelBottom = IntToString(nModelNumber); + // Row 55 + NuiSetBind(oPC, nToken, "top_title_label", JsonString("Top")); + // Row 56 + //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString(sModelTop)); + NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(TRUE)); + // Row 57 + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Middle")); + // Row 58 + //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelMiddle)); + NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); + // Row 59 + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("Bottom")); + // Row 510 + //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString(sModelBottom)); + NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(TRUE)); + // Row 511 + NuiSetBind(oPC, nToken, "btn_randomize_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_randomize_tooltip", JsonString(" Randomize the selected weapon")); + } + // Armor and clothing + else if(nItem == 0) + { + nSelected = GetArmorModelSelected(oPC); + // These models only have one side so make sure we are not linked. + if (nSelected == ITEM_APPR_ARMOR_MODEL_NECK || + nSelected == ITEM_APPR_ARMOR_MODEL_TORSO || + nSelected == ITEM_APPR_ARMOR_MODEL_BELT || + nSelected == ITEM_APPR_ARMOR_MODEL_PELVIS || + nSelected == ITEM_APPR_ARMOR_MODEL_ROBE) + { + sModelMiddle = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nSelected)); + // Row 55 + NuiSetBind(oPC, nToken, "top_title_label", JsonString("")); + // Row 56 + //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "txt_model_name_t", JsonString("")); + NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(FALSE)); + // Row 57 + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Model")); + // Row 58 + //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelMiddle)); + NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); + // Row 59 + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("")); + // Row 510 + //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString("")); + NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(FALSE)); + } + else + { + sModelTop = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nSelected)); + if(nSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nSelected--; + else nSelected++; + sModelBottom = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nSelected)); + // Row 55 + NuiSetBind(oPC, nToken, "top_title_label", JsonString("Right")); + // Row 56 + //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString(sModelTop)); + NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(TRUE)); + // Row 57 + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Right & Left")); + // Row 58 + //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelTop)); + NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); + // Row 59 + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("Left")); + // Row 510 + //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString(sModelBottom)); + NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(TRUE)); + } + // Row 511 + NuiSetBind(oPC, nToken, "btn_randomize_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_randomize_tooltip", JsonString(" Randomize the selected armor")); + } + // Shields, Cloaks, and Helmets. + else + { + sModelMiddle = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0)); + // Row 55 + NuiSetBind(oPC, nToken, "top_title_label", JsonString("")); + // Row 56 + //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString("")); + NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(FALSE)); + // Row 57 + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Model")); + // Row 58 + //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelMiddle)); + NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); + // Row 59 + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("")); + // Row 510 + //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString("")); + NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(FALSE)); + // Row 511 + NuiSetBind(oPC, nToken, "btn_randomize_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_randomize_tooltip", JsonString(" Randomize the selected item")); + } + // Color Group + if(ai_GetIsWeapon(oItem) || ai_GetIsShield(oItem)) + { + // Need to disable the color widgets. + // Row 511 + NuiSetBind(oPC, nToken, "color_pallet_image", JsonString("gui_pal_tattoo")); + NuiSetBind(oPC, nToken, "color_pallet_image_event", JsonBool(FALSE)); + // Row 512 - Label Part to Color + // Row 5l3 + NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(FALSE)); + // Row 514 - Label Part Color to Reset + // Row 515 + NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(FALSE)); + // Row 516 - Label Material to Color + // Row 517 + NuiSetBind(oPC, nToken, "btn_material_0", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_material_2", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_material_4", JsonBool(FALSE)); + // Row 518 + NuiSetBind(oPC, nToken, "btn_material_1", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_material_3", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_material_5", JsonBool(FALSE)); + SetMaterialButtons(oPC, nToken, -1); + } + // Armor and clothing + else if(nItem == 0) + { + // Row 511 + string sColorPallet = GetLocalString(oPC, CRAFT_COLOR_PALLET); + if(sColorPallet == "") sColorPallet = "gui_pal_tattoo"; + int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + int nModelSelected = GetArmorModelSelected(oPC); + // Row 511 + NuiSetBind(oPC, nToken, "color_pallet_image", JsonString(sColorPallet)); + NuiSetBind(oPC, nToken, "color_pallet_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "color_pallet_tooltip", JsonString(" Select a color or use the mouse wheel")); + int nSelectedRight, nSelectedAll, nSelectedLeft; + string sColorAll = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected)); + // These models only have one side so make sure we are not linked. + if (nModelSelected == ITEM_APPR_ARMOR_MODEL_NECK || + nModelSelected == ITEM_APPR_ARMOR_MODEL_TORSO || + nModelSelected == ITEM_APPR_ARMOR_MODEL_BELT || + nModelSelected == ITEM_APPR_ARMOR_MODEL_PELVIS || + nModelSelected == ITEM_APPR_ARMOR_MODEL_ROBE) + { + // Row 512 - Label Part to Color + // Row 5l3 + int nPartColor = GetHasPartColor(oItem, nModelSelected, "Right"); + nSelectedRight = JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR)); + if(!nSelectedRight && nPartColor) + { + nSelectedRight = TRUE; + nSelectedLeft = FALSE; + } + nSelectedAll = !nSelectedRight; + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonBool(nSelectedAll)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonBool(nSelectedRight)); + NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(nSelectedRight)); + NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(nSelectedAll)); + NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(FALSE)); + // Row 514 - Label Part Color to Reset + // Row 5l5 + nSelectedRight = GetHasPartColor(oItem, nModelSelected, "Right"); + nSelectedAll = nSelectedRight; + NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(nSelectedRight)); + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(nSelectedAll)); + NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(FALSE)); + } + else + { + // Row 512 - Label Part to Color + // Row 5l3 + int nPartColor = GetHasPartColor(oItem, nModelSelected, "Right"); + nSelectedRight = JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR)); + if(!nSelectedRight && nPartColor) + { + nSelectedRight = TRUE; + nSelectedLeft = FALSE; + } + else + { + nPartColor = GetHasPartColor(oItem, nModelSelected, "Left"); + nSelectedLeft = JsonGetInt(JsonObjectGet(jCraft, CRAFT_LEFT_PART_COLOR)); + if(!nSelectedLeft && nPartColor) + { + nSelectedLeft = TRUE; + nSelectedRight = FALSE; + } + } + nSelectedAll = !nSelectedRight && !nSelectedLeft; + jCraft = JsonObjectSet(jCraft, CRAFT_LEFT_PART_COLOR, JsonBool(nSelectedLeft)); + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonBool(nSelectedAll)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonBool(nSelectedRight)); + NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(nSelectedRight)); + NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(nSelectedAll)); + NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(nSelectedLeft)); + NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(TRUE)); + // Row 514 - Label Part Color to Reset + // Row 5l5 + nSelectedRight = GetHasPartColor(oItem, nModelSelected, "Right"); + nSelectedLeft = GetHasPartColor(oItem, nModelSelected, "Left"); + nSelectedAll = nSelectedRight || nSelectedLeft; + NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(nSelectedRight)); + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(nSelectedAll)); + NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(nSelectedLeft)); + } + int nColor; + if(!JsonGetInt(NuiGetBind(oPC, nToken, "btn_all_color"))) + { + int nModelSelected = GetArmorModelSelected(oPC); + if(!JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR))) + { + // Note: Right Thigh and Left Thigh are backwards so this fixes that! + if (nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected--; + else nModelSelected++; + } + int nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex); + } + else nColor = 255; + if(nColor == 255) nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected); + float fPointX = IntToFloat((nColor - ((nColor / 16) * 16)) * 20); + float fPointY = IntToFloat((nColor / 16) * 20); + NuiSetBind(oPC, nToken, "color_pallet_pointer", NuiRect(fPointX, fPointY, 20.0, 20.0)); + // Row 516 - Label Material to Color + // Row 517 & 518 + NuiSetBind(oPC, nToken, "btn_right_part_color_tooltip", JsonString(" Select the right part to be uniquely colored")); + NuiSetBind(oPC, nToken, "btn_all_color_tooltip", JsonString(" Select all parts to be colored")); + NuiSetBind(oPC, nToken, "btn_left_part_color_tooltip", JsonString(" Select the left part to be uniquely colored")); + NuiSetBind(oPC, nToken, "btn_right_part_reset_tooltip", JsonString(" Clears the right part's unique color")); + NuiSetBind(oPC, nToken, "btn_all_reset_tooltip", JsonString(" Clears all parts unique colors")); + NuiSetBind(oPC, nToken, "btn_left_part_reset_tooltip", JsonString(" Clears the left part's unique color")); + nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + SetMaterialButtons(oPC, nToken, nSelected); + SetLocalJson(oPC, CRAFT_JSON, jCraft); + } + // Cloaks and Helmets. + else + { + // Row 511 + string sColorPallet = GetLocalString(oPC, CRAFT_COLOR_PALLET); + if(sColorPallet == "") sColorPallet = "gui_pal_tattoo"; + int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + int nModelSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MODEL_SELECTION)); + int nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected); + float fPointX = IntToFloat((nColor - ((nColor / 16) * 16)) * 20); + float fPointY = IntToFloat((nColor / 16) * 20); + NuiSetBind(oPC, nToken, "color_pallet_pointer", NuiRect(fPointX, fPointY, 20.0, 20.0)); + NuiSetBind(oPC, nToken, "color_pallet_image", JsonString(sColorPallet)); + NuiSetBind(oPC, nToken, "color_pallet_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "color_pallet_tooltip", JsonString(" Select a color or use the mouse wheel")); + // Row 512 - Label Part to Color + // Row 5l3 + NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); + // Row 514 - Label Part Color to Reset + // Row 5l5 + NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(FALSE)); + //NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(FALSE)); + // Row 516 - Label Material to Color + // Row 517 & 518 + nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + SetMaterialButtons(oPC, nToken, nSelected); + } + // Lets make sure we clean up any cool down variables. + //DeleteLocalInt(oPC, CRAFT_COOL_DOWN); +} +int IfOnBannedBaseItemTypeList(object oPC, object oItem) +{ + int nIndex, nBaseItemType = GetBaseItemType(oItem); + int nBannedBaseItemType = StringToInt(ai_GetStringArray(CRAFT_BANNED_BASEITEMTYPES, nIndex)); + while(nBannedBaseItemType) + { + if(nBaseItemType == nBannedBaseItemType) + { + ai_SendMessages(GetName(oItem) + " cannot have it's appearance changed!", AI_COLOR_RED, oPC); + return TRUE; + } + nBannedBaseItemType = StringToInt(ai_GetStringArray(CRAFT_BANNED_BASEITEMTYPES, ++nIndex)); + } + return FALSE; } - diff --git a/src/module/nss/pe_debug.nss b/src/module/nss/pe_debug.nss index 5b6ad1a..dcc254c 100644 --- a/src/module/nss/pe_debug.nss +++ b/src/module/nss/pe_debug.nss @@ -57,13 +57,10 @@ void main() } ai_SendMessages("Your reputation with " + GetName(oTarget) + " has been set to neutral.", AI_COLOR_YELLOW, oPC); } - else if(sTargetMode == "SET_REPUTATION") + else if(sTargetMode == "CLEAR_COMMANDABLE") { - SetStandardFactionReputation(STANDARD_FACTION_COMMONER, 50, oTarget); - SetStandardFactionReputation(STANDARD_FACTION_DEFENDER, 50, oTarget); - SetStandardFactionReputation(STANDARD_FACTION_HOSTILE, 50, oTarget); - SetStandardFactionReputation(STANDARD_FACTION_MERCHANT, 50, oTarget); - ai_SendMessages(GetName(oTarget) + " has been set to a neutral reputation.", AI_COLOR_YELLOW, oPC); + SetCommandable(TRUE, oTarget); + ai_SendMessages(GetName(oTarget) + " has been set to be commandable.", AI_COLOR_YELLOW, oPC); } else if(sTargetMode == "DEBUG_INFO") { @@ -78,9 +75,14 @@ void main() int nObjectType = GetObjectType(oTarget); if(nObjectType == OBJECT_TYPE_CREATURE) { + string sText = "Yes"; + if(!GetCommandable(oTarget)) sText = "No"; + ai_SendMessages("Commandable: " + sText, AI_COLOR_WHITE, oPC); json jObject = ObjectToJson(oTarget); string sConversation = JsonGetString(GffGetResRef(jObject, "Conversation")); ai_SendMessages("Conversation: " + sConversation, AI_COLOR_CYAN, oPC); + int nCurrentAction = GetCurrentAction(oTarget); + ai_SendMessages("Current Action: " + IntToString(nCurrentAction), AI_COLOR_RED, oPC); SendMessageToPC(oPC, "Creature Event Scripts:"); string sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; @@ -507,14 +509,14 @@ void main() ai_SendMessages("Select an npc to change scripts for.", AI_COLOR_YELLOW, oPC); EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE , MOUSECURSOR_CREATE, MOUSECURSOR_NOCREATE); } - else if(sElem == "btn_set_reputation") + else if(sElem == "btn_set_commandable") { // Set this variable on the player so PEPS can run the targeting script for this plugin. SetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT, "pe_debug"); // Set Targeting variables. - SetLocalString(oPC, AI_TARGET_MODE, "SET_REPUTATION"); + SetLocalString(oPC, AI_TARGET_MODE, "CLEAR_COMMANDABLE"); NuiDestroy(oPC, nToken); - ai_SendMessages("Select a creature to set all standard reputations to neutral.", AI_COLOR_YELLOW, oPC); + ai_SendMessages("Select a creature to set commandable.", AI_COLOR_YELLOW, oPC); EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); } else if(sElem == "btn_clear_reputation") diff --git a/src/module/nss/pe_henchmen.nss b/src/module/nss/pe_henchmen.nss index 4f6bf42..6d4412e 100644 --- a/src/module/nss/pe_henchmen.nss +++ b/src/module/nss/pe_henchmen.nss @@ -32,6 +32,7 @@ void main() vector vTarget = GetTargetingModeSelectedPosition(); location lLocation = Location(GetArea(oPC), vTarget, GetFacing(oPC)); object oObject = GetLocalObject(oPC, "AI_TARGET_OBJECT"); + DeleteLocalString(oPC, AI_TARGET_MODE); // If the user manually exited targeting mode without selecting a target, return if(!GetIsObjectValid(oTarget) && vTarget == Vector()) { @@ -327,6 +328,7 @@ void main() if(JsonGetType(jLvlStatList) == JSON_TYPE_NULL) { RemoveHenchman(oPC, oHenchman); + ChangeToStandardFaction(oHenchman, STANDARD_FACTION_DEFENDER); // Make sure to get a clean faction version of the henchman here. jHenchman = ObjectToJson(oHenchman, TRUE); jHenchman = CreateLevelStatList(jHenchman, oHenchman, oPC); diff --git a/src/module/nss/pi_buffing.nss b/src/module/nss/pi_buffing.nss index 6ce454e..18e40a3 100644 --- a/src/module/nss/pi_buffing.nss +++ b/src/module/nss/pi_buffing.nss @@ -37,6 +37,9 @@ void PopupWidgetBuffGUIPanel(object oPC); void main() { object oPC = OBJECT_SELF; + // Set window to not save until it has been created. + SetLocalInt(oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand(0.5f, DeleteLocalInt(oPC, AI_NO_NUI_SAVE)); // Check to make sure the database is setup before we do anything. CheckBuffDataAndInitialize(oPC, "menudata"); json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); @@ -54,22 +57,30 @@ void main() if(StartingUp(oPC)) return; // Row 1 (Buttons) ********************************************************* 83 json jRow = CreateButtonSelect(JsonArray(), "Save", "btn_save", 60.0f, 30.0f, "btn_save_tooltip"); - CreateButton(jRow, "Clear", "btn_clear", 60.0f, 30.0f, -1.0, "btn_clear_tooltip"); - CreateButton(jRow, "Buff", "btn_buff", 60.0f, 30.0f, -1.0, "btn_buff_tooltip"); - CreateButtonSelect(jRow, "List 1", "btn_list1", 60.0f, 30.0f); - CreateButtonSelect(jRow, "List 2", "btn_list2", 60.0f, 30.0f); - CreateButtonSelect(jRow, "List 3", "btn_list3", 60.0f, 30.0f); - CreateButtonSelect(jRow, "List 4", "btn_list4", 60.0f, 30.0f); + jRow = CreateButton(jRow, "Clear", "btn_clear", 60.0f, 30.0f, -1.0, "btn_clear_tooltip"); + jRow = CreateButton(jRow, "Buff", "btn_buff", 60.0f, 30.0f, -1.0, "btn_buff_tooltip"); + jRow = CreateButtonSelect(jRow, "List 1", "btn_list1", 60.0f, 30.0f); + jRow = CreateButtonSelect(jRow, "List 2", "btn_list2", 60.0f, 30.0f); + jRow = CreateButtonSelect(jRow, "List 3", "btn_list3", 60.0f, 30.0f); + jRow = CreateButtonSelect(jRow, "List 4", "btn_list4", 60.0f, 30.0f); + jRow = CreateTextEditBox(jRow, "", "txt_spell_delay", 3, FALSE, 40.0f, 30.0f, "txt_spell_delay_tooltip"); // Add the row to the column. json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); // Row 2 (Buttons) ********************************************************* 121 jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); jRow = CreateCheckBox(jRow, "Buff Widget", "buff_widget", 110.0, 30.0f, "buff_widget_tooltip"); jRow = CreateCheckBox(jRow, "Lock Widget", "lock_buff_widget", 110.0, 30.0f, "lock_buff_widget_tooltip"); - if(!AI_SERVER) + if(!ai_GetIsServer()) { jRow = CreateCheckBox(jRow, "Don't Check for Monsters", "chbx_no_monster_check", 200.0, 30.0f, "chbx_no_monster_check_tooltip"); } + else + { + if(ai_GetIsDungeonMaster(oPC)) + { + jRow = CreateCheckBox(jRow, "Don't Check for Monsters", "chbx_no_monster_check", 200.0, 30.0f, "chbx_no_monster_check_tooltip"); + } + } jRow = JsonArrayInsert(jRow, NuiSpacer()); // Add the row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); @@ -96,7 +107,7 @@ void main() jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Get the window location to restore it from the database. float fWidth = IntToFloat(nIndex) * 39; - if(fWidth < 470.0) fWidth = 470.0; + if(fWidth < 530.0) fWidth = 530.0; float fX = JsonGetFloat(JsonArrayGet(jMenuData, 1)); float fY = JsonGetFloat(JsonArrayGet(jMenuData, 2)); if(fX == 0.0f && fY == 0.0f) @@ -108,6 +119,8 @@ void main() json jLayout = NuiCol(jCol); int nToken = SetWindow(oPC, jLayout, "plbuffwin", "Fast Buffing Spells", fX, fY, fWidth, 164.0, FALSE, FALSE, TRUE, FALSE, TRUE, "pe_buffing"); + // Set event watches for window inspector and save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); // Set the elements to show events. int nSelected = GetEventScript(oPC, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT) == "pc_savebuffs"; NuiSetBind(oPC, nToken, "btn_save", JsonBool(nSelected)); @@ -131,11 +144,22 @@ void main() if(sList == "list4") NuiSetBind (oPC, nToken, "btn_list4", JsonBool (TRUE)); else NuiSetBind (oPC, nToken, "btn_list4", JsonBool(FALSE)); NuiSetBind(oPC, nToken, "btn_list4_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "txt_spell_delay", TRUE); + string sText = " Allows you to adjust the speed that spells are cast in [0.1 seconds to 6.0 seconds]"; + NuiSetBind(oPC, nToken, "txt_spell_delay_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_spell_delay_tooltip", JsonString(sText)); + CheckBuffDataAndInitialize(oPC, "Delay"); + sText = GetBuffDatabaseString(oPC, "spells", "Delay"); + float fDelay = StringToFloat(sText); + if(fDelay < 0.1f) fDelay = 0.1f; + else if(fDelay > 6.0f) fDelay = 6.0f; + sText = FloatToString(fDelay, 0, 1); + NuiSetBind(oPC, nToken, "txt_spell_delay", JsonString(sText)); int nValue = JsonGetInt(JsonArrayGet(jMenuData, 3)); NuiSetBind(oPC, nToken, "buff_widget_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "buff_widget_check", JsonBool(nValue)); NuiSetBindWatch(oPC, nToken, "buff_widget_check", TRUE); - string sText = " Creates a set of 4 buttons on the screen for quick buffing."; + sText = " Creates a set of 4 buttons on the screen for quick buffing."; NuiSetBind(oPC, nToken, "buff_widget_tooltip", JsonString(sText)); nValue = JsonGetInt(JsonArrayGet(jMenuData, 4)); NuiSetBind(oPC, nToken, "lock_buff_widget_event", JsonBool(TRUE)); @@ -143,7 +167,7 @@ void main() NuiSetBindWatch(oPC, nToken, "lock_buff_widget_check", TRUE); sText = " Locks the buffing widget in place reducing its size."; NuiSetBind(oPC, nToken, "lock_buff_widget_tooltip", JsonString(sText)); - if(!AI_SERVER) + if(!ai_GetIsServer()) { NuiSetBind(oPC, nToken, "chbx_no_monster_check_event", JsonBool(TRUE)); nValue = GetLocalInt(oPC, FB_NO_MONSTER_CHECK); @@ -154,7 +178,7 @@ void main() } // Create buttons with spells listed. int nSpell, nClass, nLevel, nMetamagic, nDomain; - string sName, sTargetName, sResRef; + string sName, sTargetName, sCasterName, sResRef; nCntr = 0; nIndex = 0; while(nCntr <= BUFF_MAX_SPELLS) @@ -167,11 +191,13 @@ void main() nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); nMetamagic = JsonGetInt(JsonArrayGet(jSpell, 3)); nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); - sTargetName = JsonGetString(JsonArrayGet(jSpell, 5)); + sCasterName = JsonGetString(JsonArrayGet(jSpell, 5)); + sTargetName = JsonGetString(JsonArrayGet(jSpell, 6)); sResRef = Get2DAString("spells", "IconResRef", nSpell); - sName = " " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); - sName += " (" + GetStringByStrRef(StringToInt(Get2DAString("classes", "Short", nClass))); - sName += " / " + IntToString (nLevel); + sName = " " + sCasterName + " ("; + sName += GetStringByStrRef(StringToInt(Get2DAString("classes", "Short", nClass))); + sName += " / " + IntToString (nLevel) + ") casting "; + sName += GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); if(nMetamagic > 0) { if(nMetamagic == METAMAGIC_EMPOWER) sName += " / Empowered"; @@ -182,7 +208,7 @@ void main() else if(nMetamagic == METAMAGIC_STILL) sName += " / Still"; } if(nDomain > 0) sName += " / Domain"; - sName += ") " + sTargetName; + sName += " on " + sTargetName; sIndex = IntToString(nIndex++); NuiSetBind(oPC, nToken, "btn_spell_" + sIndex + "_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_spell_" + sIndex + "_image", JsonString(sResRef)); @@ -190,7 +216,6 @@ void main() } nCntr++; } - NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); } int StartingUp(object oPC) { @@ -295,44 +320,55 @@ void PopupWidgetBuffGUIPanel(object oPC) SetLocalInt(oPC, AI_NO_NUI_SAVE, TRUE); DelayCommand(0.5f, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); // Row 1 (buttons)********************************************************** - json jRow = JsonArray(); - CreateButtonImage(jRow, "ir_level1", "btn_one", 35.0f, 35.0f, 0.0); - CreateButtonImage(jRow, "ir_level2", "btn_two", 35.0f, 35.0f, 0.0); - CreateButtonImage(jRow, "ir_level3", "btn_three", 35.0f, 35.0f, 0.0); - CreateButtonImage(jRow, "ir_level4", "btn_four", 35.0f, 35.0f, 0.0); + json jRow = CreateButtonImage(JsonArray(), "ir_level1", "btn_one", 35.0f, 35.0f, 0.0); + jRow = CreateButtonImage(jRow, "ir_level2", "btn_two", 35.0f, 35.0f, 0.0); + jRow = CreateButtonImage(jRow, "ir_level3", "btn_three", 35.0f, 35.0f, 0.0); + jRow = CreateButtonImage(jRow, "ir_level4", "btn_four", 35.0f, 35.0f, 0.0); // Add the row to the column. json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); - json jWidget = GetBuffDatabaseJson(oPC, "spells", "menudata"); - int bAIBuffWidgetLock = JsonGetInt(JsonArrayGet(jWidget, 4)); + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + int bAIBuffWidgetLock = JsonGetInt(JsonArrayGet(jMenuData, 4)); // Get the window location to restore it from the database. - float fX = JsonGetFloat(JsonArrayGet(jWidget, 5)); - float fY = JsonGetFloat(JsonArrayGet(jWidget, 6)); + float fX = JsonGetFloat(JsonArrayGet(jMenuData, 5)); + float fY = JsonGetFloat(JsonArrayGet(jMenuData, 6)); if(fX == 0.0f && fY == 0.0f) { fX = 10.0f; fY = 10.0f; } + float fGUI_Scale = IntToFloat(GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_SCALE)) / 100.0; if(bAIBuffWidgetLock) { - fX = fX + 4.0f; - fY = fY + 45.0f; + fX += 4.0f; + // GUI scales are a mess, I just figured them out per scale to keep the widget from moving. + if(fGUI_Scale == 1.0) fY += 37.0; + else if(fGUI_Scale == 1.1) fY += 38.0; + else if(fGUI_Scale == 1.2) fY += 40.0; + else if(fGUI_Scale == 1.3) fY += 42.0; + else if(fGUI_Scale == 1.4) fY += 43.0; + else if(fGUI_Scale == 1.5) fY += 45.0; + else if(fGUI_Scale == 1.6) fY += 47.0; + else if(fGUI_Scale == 1.7) fY += 48.0; + else if(fGUI_Scale == 1.8) fY += 50.0; + else if(fGUI_Scale == 1.9) fY += 52.0; + else if(fGUI_Scale == 2.0) fY += 54.0; } // Set the layout of the window. - json jLayout = NuiCol (jCol); + json jLayout = NuiCol(jCol); int nToken; - if(bAIBuffWidgetLock) nToken = SetWindow(oPC, jLayout, "widgetbuffwin", "Fast Buff Widget", fX, fY, 160.0, 62.0, FALSE, FALSE, FALSE, TRUE, FALSE, "pe_buffing"); - else nToken = SetWindow(oPC, jLayout, "widgetbuffwin", "Fast Buff Widget", fX, fY, 160.0, 95.0, FALSE, FALSE, FALSE, TRUE, TRUE, "pe_buffing"); + if(bAIBuffWidgetLock) nToken = SetWindow (oPC, jLayout, "widgetbuffwin", "Fast Buff Widget", fX, fY, 160.0, 62.0, FALSE, FALSE, FALSE, TRUE, FALSE, "pe_buffing"); + else nToken = SetWindow (oPC, jLayout, "widgetbuffwin", "Fast Buff Widget", fX, fY, 160.0, 95.0, FALSE, FALSE, FALSE, TRUE, TRUE, "pe_buffing"); // Set event watches for window inspector and save window location. - //NuiSetBindWatch(oPC, nToken, "collapsed", TRUE); - NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + NuiSetBindWatch (oPC, nToken, "collapsed", TRUE); + NuiSetBindWatch (oPC, nToken, "window_geometry", TRUE); // Set the buttons to show events. //NuiSetBind (oPC, nToken, "btn_one", JsonBool (TRUE)); - NuiSetBind(oPC, nToken, "btn_one_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_two", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_two_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_three", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_three_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_four", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_four_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_one_event", JsonBool (TRUE)); + NuiSetBind (oPC, nToken, "btn_two", JsonBool (TRUE)); + NuiSetBind (oPC, nToken, "btn_two_event", JsonBool (TRUE)); + NuiSetBind (oPC, nToken, "btn_three", JsonBool (TRUE)); + NuiSetBind (oPC, nToken, "btn_three_event", JsonBool (TRUE)); + NuiSetBind (oPC, nToken, "btn_four", JsonBool (TRUE)); + NuiSetBind (oPC, nToken, "btn_four_event", JsonBool (TRUE)); } diff --git a/src/module/nss/pi_crafting.nss b/src/module/nss/pi_crafting.nss index 3fa9775..7df0bec 100644 --- a/src/module/nss/pi_crafting.nss +++ b/src/module/nss/pi_crafting.nss @@ -8,693 +8,20 @@ #include "0i_nui" #include "0i_items" #include "nw_inc_gff" -const string CRAFT_JSON = "CRAFT_JSON"; -const string CRAFT_COOL_DOWN = "CRAFT_COOL_DOWN"; -const string CRAFT_ITEM_SELECTION = "CRAFT_ITEM_SELECTION"; -const string CRAFT_MATERIAL_SELECTION = "CRAFT_MATERIAL_SELECTION"; -const string CRAFT_MODEL_SELECTION = "CRAFT_MODEL_SELECTION"; -const string CRAFT_COLOR_PALLET = "CRAFT_COLOR_PALLET"; -const string CRAFT_LEFT_PART_COLOR = "CRAFT_LEFT_PART_COLOR"; -const string CRAFT_ALL_COLOR = "CRAFT_ALL_COLOR"; -const string CRAFT_RIGHT_PART_COLOR = "CRAFT_RIGHT_PART_COLOR"; -const string CRAFT_TARGET = "CRAFT_TARGET"; -// Tag used in lighting effects. -const string CRAFT_HIGHLIGHT = "CRAFT_HIGHLIGHT"; -const string CRAFT_ULTRALIGHT = "CRAFT_ULTRALIGHT"; -json CreateItemCombo(object oPC, json jRow, string sComboBind); -json CreateModelCombo(object oPC, object oTarget, json jRow, string sComboBind); -json CreateMaterialCombo(object oPC, json jRow, string sComboBind); -// Sets the material buttons for use. -// nMaterial 0,1 Cloth 2,3 Leather 4,5 Metal -1 None. -void SetMaterialButtons(object oPC, int nToken, int nMaterial); -// Returns the correct item based on the crafting menu selected item. -object GetSelectedItem(object oTarget, int nItemSelected); -int GetArmorModelSelected(object oPC); -// Returns True if oItem, nPart has a per part color for sSide. -int GetHasPartColor(object oItem, int nPart, string sSide); // Does startup check if the game has just been loaded. int StartingUp(object oPC); void main() { object oPC = OBJECT_SELF; - object oTarget = GetLocalObject(oPC, CRAFT_TARGET); - if(oTarget == OBJECT_INVALID) oTarget = oPC; if(StartingUp(oPC)) return; - json jCraft = GetLocalJson(oPC, CRAFT_JSON); - // Row 1 (Object Name)****************************************************** 508 / 83 - json jRow = CreateTextEditBox(JsonArray(), "plc_hold_bind", "txt_item_name", 50, FALSE, 486.0f, 30.0f); // 419 - json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); - // Row 2 (Object Name)****************************************************** 508 / 121 - jRow = JsonArray(); - if(!AI_SERVER) jRow = CreateButton(jRow, "Information", "btn_info", 160.0f, 30.0f, -1.0, "btn_info_tooltip"); - else - { - if(GetIsDM(oTarget)) - { - jRow = CreateButton(jRow, "Information", "btn_info", 160.0f, 30.0f, -1.0, "btn_info_tooltip"); - } - else jRow = JsonArrayInsert(jRow, NuiSpacer()); - } - jRow = CreateButton(jRow, "Wardrobe", "btn_wardrobe", 158.0f, 30.0f, -1.0, "btn_wardrobe_tooltip"); - jRow = CreateButtonSelect(jRow, "Add Light", "btn_highlight", 160.0f, 30.0f, "btn_highlight_tooltip"); - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 3 (Object Name)****************************************************** 508 / 159 - jRow = CreateButton(JsonArray(), "Save", "btn_save", 160.0f, 30.0f, -1.0, "btn_save_tooltip"); - jRow = CreateButton(jRow, "Select Target", "btn_select_target", 158.0f, 30.0f); - jRow = CreateButton(jRow, "", "btn_cancel", 160.0f, 30.0f, -1.0, "btn_cancel_tooltip"); - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 4 (labels)*********************************************************** 508 / 177 - jRow = CreateLabel(JsonArray(), "Model", "module_title", 143.0f, 10.0f); - jRow = CreateLabel(jRow, "Color", "color_title", 339.0f, 10.0f); - jRow = JsonArrayInsert(jCol, NuiRow(jRow)); - // Row 5 (groups) - // Row 51 (title)*********************************************************** 508 / 195 / 18 - json jGroupRow = CreateLabel(JsonArray(), "Item", "item__cmb_title", 128.0f, 10.0f); - json jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); - // Row 52 (combo)*********************************************************** 508 / 233 / 56 - jGroupRow = CreateItemCombo(oPC, JsonArray(), "item_combo"); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 53 (title)*********************************************************** 508 / 251 / 74 - jGroupRow = CreateLabel(JsonArray(), "Model", "model_cmb_title",128.0f, 10.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 54 (combo)*********************************************************** 508 / 289 / 112 - jGroupRow = CreateModelCombo(oPC, oTarget, JsonArray(), "model_combo"); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 55 (title)*********************************************************** 508 / 307 / 120 - jGroupRow = CreateLabel(JsonArray(), "", "top_title",128.0f, 10.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 56 (top)************************************************************* 508 / 355 / 168 - jGroupRow = CreateButtonImage(JsonArray(), "nui_shld_left", "btn_prev_t", 40.0f, 40.0f); - // Removed TextEditBox for mobile - jGroupRow = CreateTextEditBox(jGroupRow, "place_holder", "txt_model_number_t", 3, FALSE, 40.0, 40.0); - //CreateLabel(jGroupRow, "", "txt_model_number_t", 40.0, 40.0); - jGroupRow = CreateButtonImage(jGroupRow, "nui_shld_right", "btn_next_t", 40.0f, 40.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 57 (title)*********************************************************** 508 / 373 / 186 - jGroupRow = CreateLabel(JsonArray(), "", "middle_title",128.0f, 10.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 58 (middle)********************************************************** 508 / 421 /234 - jGroupRow = CreateButtonImage(JsonArray(), "nui_shld_left", "btn_prev_m", 40.0f, 40.0f); - // Removed TextEditBox for mobile - jGroupRow = CreateTextEditBox(jGroupRow, "place_holder", "txt_model_number_m", 3, FALSE, 40.0, 40.0); - //CreateLabel(jGroupRow, "", "txt_model_number_m", 40.0, 40.0); - jGroupRow = CreateButtonImage(jGroupRow, "nui_shld_right", "btn_next_m", 40.0f, 40.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 59 (title)*********************************************************** 508 / 439 / 252 - jGroupRow = CreateLabel(JsonArray(), "", "bottom_title",128.0f, 10.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 510 (bottom)********************************************************* 508 / 487 /300 - jGroupRow = CreateButtonImage(JsonArray(), "nui_shld_left", "btn_prev_b", 40.0f, 40.0f); - // Removed TextEditBox for mobile - jGroupRow = CreateTextEditBox(jGroupRow, "place_holder", "txt_model_number_b", 3, FALSE, 40.0, 40.0); - //CreateLabel(jGroupRow, "", "txt_model_number_b", 40.0, 40.0); - jGroupRow = CreateButtonImage(jGroupRow, "nui_shld_right", "btn_next_b", 40.0f, 40.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 511 (blank spacer) - jGroupRow = CreateLabel(JsonArray(), "", "blank_space",128.0f, 20.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 512 (light)********************************************************** 508 / 487 /300 - jGroupRow = CreateButtonSelect(JsonArray(), "Randomize", "btn_randomize", 128.0f, 30.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - jGroupCol = JsonArrayInsert(jGroupCol, NuiSpacer()); - jRow = JsonArrayInsert(JsonArray(), NuiHeight(NuiWidth(NuiGroup(NuiCol(jGroupCol)), 143.0), 442.0)); - // Make the Color Group. - // Row 550 (groups)********************************************************* 508 / 361 / 184 - json jImage = NuiEnabled(NuiId(NuiImage(NuiBind("color_pallet_image"), JsonInt(0), JsonInt(0), JsonInt(1)), "color_pallet"), NuiBind("color_pallet_event")); - jImage = NuiWidth(jImage, 320.0); // 256 + 64 - jImage = NuiHeight(jImage, 220.0); // 176 + 44 - jImage = NuiTooltip(jImage, NuiBind("color_pallet_tooltip")); - json jIndicator = JsonArrayInsert(JsonArray(), NuiDrawListRect(JsonBool(TRUE), NuiColor(255,255,255), JsonBool(FALSE), JsonFloat(2.0), NuiBind("color_pallet_pointer"))); - jImage = NuiDrawList(jImage, JsonBool(FALSE), jIndicator); - jGroupRow = JsonArrayInsert(JsonArray(), jImage); - jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); - // Row 551 (groups)********************************************************* 508 / 379 /202 - jGroupRow = CreateLabel(JsonArray(), "Part To Color", "lbl_color_parts", 320.0f, 10.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 552 (groups)********************************************************* 508 / 417 /240 - jGroupRow = CreateButtonSelect(JsonArray(), "Right", "btn_right_part_color", 98.0, 30.0, "btn_right_part_color_tooltip"); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButtonSelect(jGroupRow, "All", "btn_all_color", 98.0, 30.0, "btn_all_color_tooltip"); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButtonSelect(jGroupRow, "Left", "btn_left_part_color", 98.0, 30.0, "btn_left_part_color_tooltip"); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 553 (groups)********************************************************* 508 / 435 / 258 - jGroupRow = CreateLabel(JsonArray(), "Part Color To Reset", "lbl_reset_parts", 320.0f, 10.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 554 (groups)********************************************************* 508 / 473 /296 - jGroupRow = CreateButton(JsonArray(), "Right", "btn_right_part_reset", 98.0, 30.0, -1.0, "btn_right_part_reset_tooltip"); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButton(jGroupRow, "All", "btn_all_reset", 50.0, 30.0, -1.0, "btn_all_reset_tooltip"); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButton(jGroupRow, "Left", "btn_left_part_reset", 98.0, 30.0, -1.0, "btn_left_part_reset_tooltip"); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 555 (groups)********************************************************* 508 / 491 / 314 - jGroupRow = CreateLabel(JsonArray(), "Material to Color", "lbl_material_color", 320.0f, 10.0f); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 556 (groups)********************************************************* 508 / 529 /352 - jGroupRow = CreateButtonSelect(JsonArray(), "Cloth 1", "btn_material_0", 98.0, 30.0); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButtonSelect(jGroupRow, "Leather 1", "btn_material_2", 98.0, 30.0); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButtonSelect(jGroupRow, "Metal 1", "btn_material_4", 98.0, 30.0); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - // Row 557 (groups)********************************************************* 508 / 567 / 390 - jGroupRow = CreateButtonSelect(JsonArray(), "Cloth 2", "btn_material_1", 98.0, 30.0); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButtonSelect(jGroupRow, "Leather 2", "btn_material_3", 98.0, 30.0); - jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); - jGroupRow = CreateButtonSelect(jGroupRow, "Metal 2", "btn_material_5", 98.0, 30.0); - jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); - jGroupCol = JsonArrayInsert(jGroupCol, NuiSpacer()); - jRow = JsonArrayInsert(jRow, NuiHeight(NuiWidth(NuiGroup(NuiCol(jGroupCol)), 339.0), 442.0)); // 275 398 - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); - json jLayout = NuiCol(jCol); - // Get the window location to restore it from the database. - json jGeometry = JsonObjectGet(jCraft, "CRAFT_MENU"); - float fX = JsonGetFloat(JsonObjectGet(jGeometry, "x")); - float fY = JsonGetFloat(JsonObjectGet(jGeometry, "y")); - string sPCWindow; - int nToken = SetWindow(oPC, jLayout, "crafting_nui", "Crafting", - fX, fY, 508.0, 700.0, FALSE, FALSE, FALSE, FALSE, TRUE, "pe_crafting"); // 444 645 - // Set all binds, events, and watches. - NuiSetBindWatch (oPC, nToken, "window_geometry", TRUE); - int nItem = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); - object oItem = GetSelectedItem(oTarget, nItem); - // Row 1 - NuiSetBind(oPC, nToken, "txt_item_name", JsonString(GetName(oItem))); - NuiSetBind(oPC, nToken, "txt_item_name_event", JsonBool(TRUE)); - NuiSetBindWatch(oPC, nToken, "txt_item_name", TRUE); - // Row 2 - NuiSetBind(oPC, nToken, "btn_info_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_info_tooltip", JsonString(" Look at and change item information")); - NuiSetBind(oPC, nToken, "btn_wardrobe_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_wardrobe_tooltip", JsonString(" Use your wardrobe to save/load item appearances")); - int nLight = GetLocalInt(oPC, CRAFT_HIGHLIGHT) + GetLocalInt(oPC, CRAFT_ULTRALIGHT); - NuiSetBind(oPC, nToken, "btn_highlight", JsonBool(nLight)); - NuiSetBind(oPC, nToken, "btn_highlight_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_highlight_tooltip", JsonString(" Left click for White light, Right click for Ultravision")); - // Row 3 - NuiSetBind(oPC, nToken, "btn_save_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_save_tooltip", JsonString(" Save current changes")); - NuiSetBind(oPC, nToken, "btn_select_target_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_cancel_label", JsonString("Exit")); - NuiSetBind(oPC, nToken, "btn_cancel_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_cancel_tooltip", JsonString(" Exit the crafting menu")); - // Row 4 Labels. - // Row 5 Groups. - // Row 51 title. - // Row 52 - NuiSetBind(oPC, nToken, "item_combo_selected", JsonInt(nItem)); - NuiSetBind(oPC, nToken, "item_combo_event", JsonBool(TRUE)); - NuiSetBindWatch(oPC, nToken, "item_combo_selected", TRUE); - // Row 53 title. - // Row 54 - int nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MODEL_SELECTION)); - if(nItem == 1 || nItem == 2 || nItem == 4) - { - if(GetHiddenWhenEquipped(oItem)) nSelected = 1; - else nSelected = 0; - } - NuiSetBind(oPC, nToken, "model_combo_selected", JsonInt (nSelected)); - NuiSetBind(oPC, nToken, "model_combo_event", JsonBool (TRUE)); - NuiSetBindWatch(oPC, nToken, "model_combo_selected", TRUE); - // Row 55, 56, 57 titles - // Row 58 top, 59 middle, 510 bottom - string sModelTop, sModelMiddle, sModelBottom; - // Model Group - if(ai_GetIsWeapon(oItem)) - { - int nModel = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, 0); - int nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, 0); - int nModelNumber = (nModel * 10) + nColor; - sModelTop = IntToString(nModelNumber); - nModel = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, 1); - nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, 1); - nModelNumber = (nModel * 10) + nColor; - sModelMiddle = IntToString(nModelNumber); - nModel = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, 2); - nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, 2); - nModelNumber = (nModel * 10) + nColor; - sModelBottom = IntToString(nModelNumber); - // Row 55 - NuiSetBind(oPC, nToken, "top_title_label", JsonString("Top")); - // Row 56 - //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString(sModelTop)); - NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(TRUE)); - // Row 57 - NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Middle")); - // Row 58 - //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelMiddle)); - NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); - // Row 59 - NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("Bottom")); - // Row 510 - //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString(sModelBottom)); - NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(TRUE)); - // Row 511 - NuiSetBind(oPC, nToken, "btn_randomize_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_randomize_tooltip", JsonString(" Randomize the selected weapon")); - } - // Armor and clothing - else if(nItem == 0) - { - nSelected = GetArmorModelSelected(oPC); - // These models only have one side so make sure we are not linked. - if (nSelected == ITEM_APPR_ARMOR_MODEL_NECK || - nSelected == ITEM_APPR_ARMOR_MODEL_TORSO || - nSelected == ITEM_APPR_ARMOR_MODEL_BELT || - nSelected == ITEM_APPR_ARMOR_MODEL_PELVIS || - nSelected == ITEM_APPR_ARMOR_MODEL_ROBE) - { - sModelMiddle = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nSelected)); - // Row 55 - NuiSetBind(oPC, nToken, "top_title_label", JsonString("")); - // Row 56 - //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "txt_model_name_t", JsonString("")); - NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(FALSE)); - // Row 57 - NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Model")); - // Row 58 - //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelMiddle)); - NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); - // Row 59 - NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("")); - // Row 510 - //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString("")); - NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(FALSE)); - } - else - { - sModelTop = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nSelected)); - if(nSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nSelected--; - else nSelected++; - sModelBottom = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nSelected)); - // Row 55 - NuiSetBind(oPC, nToken, "top_title_label", JsonString("Right")); - // Row 56 - //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString(sModelTop)); - NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(TRUE)); - // Row 57 - NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Right & Left")); - // Row 58 - //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelTop)); - NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); - // Row 59 - NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("Left")); - // Row 510 - //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString(sModelBottom)); - NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(TRUE)); - } - // Row 511 - NuiSetBind(oPC, nToken, "btn_randomize_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_randomize_tooltip", JsonString(" Randomize the selected armor")); - } - // Shields, Cloaks, and Helmets. - else - { - sModelMiddle = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0)); - // Row 55 - NuiSetBind(oPC, nToken, "top_title_label", JsonString("")); - // Row 56 - //NuiSetBind(oPC, nToken, "txt_model_number_t_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString("")); - NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(FALSE)); - // Row 57 - NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Model")); - // Row 58 - //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelMiddle)); - NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); - // Row 59 - NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("")); - // Row 510 - //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString("")); - NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(FALSE)); - // Row 511 - NuiSetBind(oPC, nToken, "btn_randomize_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_randomize_tooltip", JsonString(" Randomize the selected item")); - } - // Color Group - if(ai_GetIsWeapon(oItem) || ai_GetIsShield(oItem)) - { - // Need to disable the color widgets. - // Row 511 - NuiSetBind(oPC, nToken, "color_pallet_image", JsonString("gui_pal_tattoo")); - NuiSetBind(oPC, nToken, "color_pallet_image_event", JsonBool(FALSE)); - // Row 512 - Label Part to Color - // Row 5l3 - NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(FALSE)); - // Row 514 - Label Part Color to Reset - // Row 515 - NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(FALSE)); - // Row 516 - Label Material to Color - // Row 517 - NuiSetBind(oPC, nToken, "btn_material_0", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_material_2", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_material_4", JsonBool(FALSE)); - // Row 518 - NuiSetBind(oPC, nToken, "btn_material_1", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_material_3", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_material_5", JsonBool(FALSE)); - SetMaterialButtons(oPC, nToken, -1); - } - // Armor and clothing - else if(nItem == 0) - { - // Row 511 - string sColorPallet = GetLocalString(oPC, CRAFT_COLOR_PALLET); - if(sColorPallet == "") sColorPallet = "gui_pal_tattoo"; - int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); - int nModelSelected = GetArmorModelSelected(oPC); - // Row 511 - NuiSetBind(oPC, nToken, "color_pallet_image", JsonString(sColorPallet)); - NuiSetBind(oPC, nToken, "color_pallet_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "color_pallet_tooltip", JsonString(" Select a color or use the mouse wheel")); - int nSelectedRight, nSelectedAll, nSelectedLeft; - string sColorAll = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected)); - // These models only have one side so make sure we are not linked. - if (nModelSelected == ITEM_APPR_ARMOR_MODEL_NECK || - nModelSelected == ITEM_APPR_ARMOR_MODEL_TORSO || - nModelSelected == ITEM_APPR_ARMOR_MODEL_BELT || - nModelSelected == ITEM_APPR_ARMOR_MODEL_PELVIS || - nModelSelected == ITEM_APPR_ARMOR_MODEL_ROBE) - { - // Row 512 - Label Part to Color - // Row 5l3 - int nPartColor = GetHasPartColor(oItem, nModelSelected, "Right"); - nSelectedRight = JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR)); - if(!nSelectedRight && nPartColor) - { - nSelectedRight = TRUE; - nSelectedLeft = FALSE; - } - nSelectedAll = !nSelectedRight; - jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonBool(nSelectedAll)); - jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonBool(nSelectedRight)); - NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(nSelectedRight)); - NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(nSelectedAll)); - NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(FALSE)); - // Row 514 - Label Part Color to Reset - // Row 5l5 - nSelectedRight = GetHasPartColor(oItem, nModelSelected, "Right"); - nSelectedAll = nSelectedRight; - NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(nSelectedRight)); - NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(nSelectedAll)); - NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(FALSE)); - } - else - { - // Row 512 - Label Part to Color - // Row 5l3 - int nPartColor = GetHasPartColor(oItem, nModelSelected, "Right"); - nSelectedRight = JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR)); - if(!nSelectedRight && nPartColor) - { - nSelectedRight = TRUE; - nSelectedLeft = FALSE; - } - else - { - nPartColor = GetHasPartColor(oItem, nModelSelected, "Left"); - nSelectedLeft = JsonGetInt(JsonObjectGet(jCraft, CRAFT_LEFT_PART_COLOR)); - if(!nSelectedLeft && nPartColor) - { - nSelectedLeft = TRUE; - nSelectedRight = FALSE; - } - } - nSelectedAll = !nSelectedRight && !nSelectedLeft; - jCraft = JsonObjectSet(jCraft, CRAFT_LEFT_PART_COLOR, JsonBool(nSelectedLeft)); - jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonBool(nSelectedAll)); - jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonBool(nSelectedRight)); - NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(nSelectedRight)); - NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(nSelectedAll)); - NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(nSelectedLeft)); - NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(TRUE)); - // Row 514 - Label Part Color to Reset - // Row 5l5 - nSelectedRight = GetHasPartColor(oItem, nModelSelected, "Right"); - nSelectedLeft = GetHasPartColor(oItem, nModelSelected, "Left"); - nSelectedAll = nSelectedRight || nSelectedLeft; - NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(nSelectedRight)); - NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(nSelectedAll)); - NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(nSelectedLeft)); - } - int nColor; - if(!JsonGetInt(NuiGetBind(oPC, nToken, "btn_all_color"))) - { - int nModelSelected = GetArmorModelSelected(oPC); - if(!JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR))) - { - // Note: Right Thigh and Left Thigh are backwards so this fixes that! - if (nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected--; - else nModelSelected++; - } - int nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; - nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex); - } - else nColor = 255; - if(nColor == 255) nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected); - float fPointX = IntToFloat((nColor - ((nColor / 16) * 16)) * 20); - float fPointY = IntToFloat((nColor / 16) * 20); - NuiSetBind(oPC, nToken, "color_pallet_pointer", NuiRect(fPointX, fPointY, 20.0, 20.0)); - // Row 516 - Label Material to Color - // Row 517 & 518 - NuiSetBind(oPC, nToken, "btn_right_part_color_tooltip", JsonString(" Select the right part to be uniquely colored")); - NuiSetBind(oPC, nToken, "btn_all_color_tooltip", JsonString(" Select all parts to be colored")); - NuiSetBind(oPC, nToken, "btn_left_part_color_tooltip", JsonString(" Select the left part to be uniquely colored")); - NuiSetBind(oPC, nToken, "btn_right_part_reset_tooltip", JsonString(" Clears the right part's unique color")); - NuiSetBind(oPC, nToken, "btn_all_reset_tooltip", JsonString(" Clears all parts unique colors")); - NuiSetBind(oPC, nToken, "btn_left_part_reset_tooltip", JsonString(" Clears the left part's unique color")); - nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); - SetMaterialButtons(oPC, nToken, nSelected); - SetLocalJson(oPC, CRAFT_JSON, jCraft); - } - // Cloaks and Helmets. - else - { - // Row 511 - string sColorPallet = GetLocalString(oPC, CRAFT_COLOR_PALLET); - if(sColorPallet == "") sColorPallet = "gui_pal_tattoo"; - int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); - int nModelSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MODEL_SELECTION)); - int nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nMaterialSelected); - float fPointX = IntToFloat((nColor - ((nColor / 16) * 16)) * 20); - float fPointY = IntToFloat((nColor / 16) * 20); - NuiSetBind(oPC, nToken, "color_pallet_pointer", NuiRect(fPointX, fPointY, 20.0, 20.0)); - NuiSetBind(oPC, nToken, "color_pallet_image", JsonString(sColorPallet)); - NuiSetBind(oPC, nToken, "color_pallet_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "color_pallet_tooltip", JsonString(" Select a color or use the mouse wheel")); - // Row 512 - Label Part to Color - // Row 5l3 - NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_right_part_color_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_all_color_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_left_part_color_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); - // Row 514 - Label Part Color to Reset - // Row 5l5 - NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(FALSE)); - //NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(FALSE)); - NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(FALSE)); - // Row 516 - Label Material to Color - // Row 517 & 518 - nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); - SetMaterialButtons(oPC, nToken, nSelected); - } - // Lets make sure we clean up any cool down variables. - //DeleteLocalInt(oPC, CRAFT_COOL_DOWN); -} -json CreateItemCombo(object oPC, json jRow, string sComboBind) -{ - int nCnt; - // Create the list. - json jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Armor", 0)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Cloak", 1)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Headgear", 2)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Right hand", 3)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Left hand", 4)); - return CreateCombo(jRow, jCombo, sComboBind, 128.0, 40.0); -} -json CreateModelCombo(object oPC, object oTarget, json jRow, string sComboBind) -{ - float fFacing = GetFacing(oTarget); - json jCombo, jCraft = GetLocalJson(oPC, CRAFT_JSON); - int nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); - // Create the list. - // Armor. - if(nSelected == 0) - { - fFacing += 180.0f; - if (fFacing > 359.0) fFacing -=359.0; - AssignCommand(oPC, SetCameraFacing(fFacing, 4.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); - jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Neck", 0)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Shoulder", 1)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Bicep", 2)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Forearm", 3)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Hand", 4)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Torso", 5)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Belt", 6)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Pelvis", 7)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Thigh", 8)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Shin", 9)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Foot", 10)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Robe", 11)); - } - // Cloak. - else if(nSelected == 1) - { - if(fFacing > 359.0) fFacing -=359.0; - AssignCommand (oPC, SetCameraFacing(fFacing, 4.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); - jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Cloak", 0)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Invisible", 1)); - } - // Headgear. - else if (nSelected == 2) - { - fFacing += 180.0f; - if(fFacing > 359.0) fFacing -=359.0; - AssignCommand(oPC, SetCameraFacing(fFacing, 2.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); - jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Headgear", 0)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Invisible", 1)); - } - // Weapon. - else if (nSelected == 3) - { - // If they are changing a bow then face the opposite side. - object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC); - int nBaseItemType = GetBaseItemType(oItem); - if(nBaseItemType == BASE_ITEM_LONGBOW || nBaseItemType == BASE_ITEM_SHORTBOW) fFacing -= 90.00; - // This will make the camera face a melee weapon. - else fFacing += 90.0; - if(fFacing > 359.0) fFacing -=359.0; - AssignCommand(oPC, SetCameraFacing(fFacing, 3.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); - jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Weapon", 0)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Acidic", 1)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Frost", 2)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Electric", 3)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Unholy", 4)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Flaming", 5)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Holy", 6)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Sonic", 7)); -} - // Weapon/Shield. - else if(nSelected == 4) - { - fFacing += 270.0f; - if(fFacing > 359.0) fFacing -=359.0; - AssignCommand(oPC, SetCameraFacing(fFacing, 3.5f, 75.0, CAMERA_TRANSITION_TYPE_VERY_FAST)); - object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC); - if(ai_GetIsShield(oItem)) - { - jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Shield", 0)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Invisible", 1)); - } - else - { - jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("Weapon", 0)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Acidic", 1)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Frost", 2)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Electric", 3)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Unholy", 4)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Flaming", 5)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Holy", 6)); - jCombo = JsonArrayInsert(jCombo, NuiComboEntry("Sonic", 7)); - } - } - return CreateCombo(jRow, jCombo, sComboBind, 128.0, 40.0); -} -void SetMaterialButtons(object oPC, int nToken, int nMaterial) -{ - int nIndex, bBool, bUseable; - string sIndex; - if(nMaterial > -1) bUseable = TRUE; - for(nIndex = 0;nIndex < 6;nIndex++) - { - if(nIndex == nMaterial) bBool = TRUE; - else bBool = FALSE; - sIndex = IntToString(nIndex); - NuiSetBind(oPC, nToken, "btn_material_" + sIndex + "_event", JsonBool(bUseable)); - NuiSetBind(oPC, nToken, "btn_material_" + sIndex, JsonBool(bBool)); - } -} -object GetSelectedItem(object oTarget, int nItemSelected) -{ - if(nItemSelected == 0) return GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); - else if(nItemSelected == 1) return GetItemInSlot(INVENTORY_SLOT_CLOAK, oTarget); - else if(nItemSelected == 2) return GetItemInSlot(INVENTORY_SLOT_HEAD, oTarget); - else if(nItemSelected == 3) return GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); - else if(nItemSelected == 4) return GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); - return OBJECT_INVALID; -} -int GetArmorModelSelected(object oPC) -{ - json jCraft = GetLocalJson(oPC, CRAFT_JSON); - int nModelSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MODEL_SELECTION)); - if(nModelSelected == 0) return ITEM_APPR_ARMOR_MODEL_NECK; - if(nModelSelected == 1) return ITEM_APPR_ARMOR_MODEL_RSHOULDER; - if(nModelSelected == 2) return ITEM_APPR_ARMOR_MODEL_RBICEP; - if(nModelSelected == 3) return ITEM_APPR_ARMOR_MODEL_RFOREARM; - if(nModelSelected == 4) return ITEM_APPR_ARMOR_MODEL_RHAND; - if(nModelSelected == 5) return ITEM_APPR_ARMOR_MODEL_TORSO; - if(nModelSelected == 6) return ITEM_APPR_ARMOR_MODEL_BELT; - if(nModelSelected == 7) return ITEM_APPR_ARMOR_MODEL_PELVIS; - if(nModelSelected == 8) return ITEM_APPR_ARMOR_MODEL_RTHIGH; - if(nModelSelected == 9) return ITEM_APPR_ARMOR_MODEL_RSHIN; - if(nModelSelected == 10) return ITEM_APPR_ARMOR_MODEL_RFOOT; - return ITEM_APPR_ARMOR_MODEL_ROBE; -} -int GetHasPartColor(object oItem, int nPart, string sSide) -{ - json jItem = ObjectToJson(oItem); - string sPartName = "APart_"; - if(sSide == "Left") - { - // Note: Right Thigh and Left Thigh are backwards so this fixes that! - if (nPart == ITEM_APPR_ARMOR_MODEL_RTHIGH) nPart--; - else nPart++; - } - sPartName += IntToString(nPart) + "_Col_"; - int nPartColor = JsonGetInt(GffGetByte(jItem, sPartName + "0")); - nPartColor += JsonGetInt(GffGetByte(jItem, sPartName + "1")); - nPartColor += JsonGetInt(GffGetByte(jItem, sPartName + "2")); - nPartColor += JsonGetInt(GffGetByte(jItem, sPartName + "3")); - nPartColor += JsonGetInt(GffGetByte(jItem, sPartName + "4")); - nPartColor += JsonGetInt(GffGetByte(jItem, sPartName + "5")); - return nPartColor; + // Set this variable on the player so PEPS can run the targeting script for this plugin. + SetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT, "pe_crafting"); + // Set Targeting variables. + SetLocalString(oPC, AI_TARGET_MODE, "SELECT_TARGET"); + ai_SendMessages("Select your charcter, a henchman or an item possessed by one.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_ITEM , MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } int StartingUp(object oPC) { diff --git a/src/module/nss/pi_debug.nss b/src/module/nss/pi_debug.nss index 6555882..506b6e7 100644 --- a/src/module/nss/pi_debug.nss +++ b/src/module/nss/pi_debug.nss @@ -16,7 +16,7 @@ void main() //SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); //DelayCommand (0.5f, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); string sText = " [Single player]"; - if(AI_SERVER) sText = " [Server]"; + if(ai_GetIsServer()) sText = " [Server]"; // ************************************************************************* Width / Height // Row 1 ******************************************************************* 500 / 73 json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); @@ -30,25 +30,31 @@ void main() // Add row to the column. jCol = JsonArrayInsert(jCol, NuiRow(jRow)); // Row 3 ******************************************************************* 500 / 101 - sText = "Monster AI (nw_c2_default1): " + ResManGetAliasFor("nw_c2_default1", RESTYPE_NCS); - jRow = CreateLabel(JsonArray(), sText, "monster_1_ai", 470.0f, 20.0f, NUI_HALIGN_CENTER); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + sText = ResManGetAliasFor("nw_c2_default1", RESTYPE_NCS); + if(sText != "") + { + jRow = CreateLabel(JsonArray(), "Monster AI (nw_c2_default1): " + sText, "monster_1_ai", 470.0f, 20.0f); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } // Row 4 ******************************************************************* 500 / 157 - sText = "Monster AI (j_ai_onheartbeat): " + ResManGetAliasFor("j_ai_onheartbeat", RESTYPE_NCS); - jRow = CreateLabel(JsonArray(), sText, "monster_2_ai", 470.0f, 20.0f, NUI_HALIGN_CENTER); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + sText = ResManGetAliasFor("j_ai_onheartbeat", RESTYPE_NCS); + if(sText != "") + { + jRow = CreateLabel(JsonArray(), "Monster AI (j_ai_onheartbeat): " + sText, "monster_2_ai", 470.0f, 20.0f); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } // Row 5 ******************************************************************* 500 / 213 - sText = "Associate AI (nw_ch_ac1): " + ResManGetAliasFor("nw_ch_ac1", RESTYPE_NCS); - jRow = CreateLabel(JsonArray(), sText, "henchman_ai", 470.0f, 20.0f, NUI_HALIGN_CENTER); - // Add row to the column. - jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + sText = ResManGetAliasFor("nw_ch_ac1", RESTYPE_NCS); + if(sText != "") + { + jRow = CreateLabel(JsonArray(), "Associate AI (nw_ch_ac1): " + sText, "henchman_ai", 470.0f, 20.0f); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } // Row 6 ******************************************************************* 500 / 241 jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); jRow = CreateButton(jRow, "Set NPC's scripts", "btn_npc_scripts", 150.0f, 20.0f, -1.0, "btn_npc_scripts_tooltip"); jRow = JsonArrayInsert(jRow, NuiSpacer()); - jRow = CreateButton(jRow, "Set Reputations", "btn_set_reputation", 150.0f, 20.0f, -1.0, "btn_set_reputation_tooltip"); + jRow = CreateButton(jRow, "Set Commandable", "btn_set_commandable", 150.0f, 20.0f, -1.0, "btn_set_commandable_tooltip"); jRow = JsonArrayInsert(jRow, NuiSpacer()); jRow = CreateButton(jRow, "Clear Party Rep.", "btn_clear_reputation", 150.0f, 20.0f, -1.0, "btn_clear_reputation_tooltip"); jRow = JsonArrayInsert(jRow, NuiSpacer()); @@ -146,8 +152,8 @@ void main() // Row 6 NuiSetBind(oPC, nToken, "btn_npc_scripts_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_npc_scripts_tooltip", JsonString(" Forces NPC to use Philos AI scripts!")); - NuiSetBind(oPC, nToken, "btn_set_reputation_event", JsonBool(TRUE)); - NuiSetBind(oPC, nToken, "btn_set_reputation_tooltip", JsonString(" Sets a creatures faction to neutral for all standard factions.")); + NuiSetBind(oPC, nToken, "btn_set_commandable_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_set_commandable_tooltip", JsonString(" Sets a creatures to commandable.")); NuiSetBind(oPC, nToken, "btn_clear_reputation_event", JsonBool(TRUE)); NuiSetBind(oPC, nToken, "btn_clear_reputation_tooltip", JsonString(" Clears the party's reputation with creature's faction.")); // Row 7 diff --git a/src/module/nss/pinc_henchmen.nss b/src/module/nss/pinc_henchmen.nss index a67141a..d5f72de 100644 --- a/src/module/nss/pinc_henchmen.nss +++ b/src/module/nss/pinc_henchmen.nss @@ -369,11 +369,12 @@ void RemoveYourHenchman(object oPC, int nToken, string sParty) else { RemoveHenchman(oPC, oHenchman); + ChangeToStandardFaction(oHenchman, STANDARD_FACTION_DEFENDER); AssignCommand(oHenchman, SetIsDestroyable(TRUE, FALSE, FALSE)); NuiDestroy(oPC, NuiFindWindow(oPC, ai_GetAssociateType(oPC, oHenchman) + AI_WIDGET_NUI)); DestroyObject(oHenchman); + ai_SendMessages(GetName(oHenchman) + " has been removed from the party!", AI_COLOR_GREEN, oPC); } - ai_SendMessages(GetName(oHenchman) + " has been removed from the party!", AI_COLOR_GREEN, oPC); NuiDestroy(oPC, nToken); ExecuteScript("pi_henchmen", oPC); } @@ -388,6 +389,7 @@ void RemoveWholeParty(object oPC, int nToken, string sParty) { ai_SendMessages(GetName(oHenchman) + " has been remove from your Party.", AI_COLOR_YELLOW, oPC); RemoveHenchman(oPC, oHenchman); + ChangeToStandardFaction(oHenchman, STANDARD_FACTION_DEFENDER); AssignCommand(oHenchman, SetIsDestroyable(TRUE, FALSE, FALSE)); NuiDestroy(oPC, NuiFindWindow(oPC, ai_GetAssociateType(oPC, oHenchman) + AI_WIDGET_NUI)); DestroyObject(oHenchman); @@ -426,10 +428,20 @@ void SaveYourHenchman(object oPC, int nToken, string sParty) ChangeToStandardFaction(oHenchman, STANDARD_FACTION_DEFENDER); json jHenchman = ObjectToJson(oHenchman, TRUE); if(!bPC) AddHenchman(oPC, oHenchman); - else DestroyObject(oHenchman); - //string sPatch = "[{\"op\":\"replace\",\"path\":\"/FactionID/value\",\"value\":1}]"; - //json jPatch = JsonParse(sPatch); - //jHenchman = JsonPatch(jHenchman, jPatch); + else + { + DestroyObject(oHenchman); + // We need to make sure the henchman is not seen as a PC or DM! + jHenchman = GffReplaceByte(jHenchman, "IsPC", 0); + jHenchman = GffReplaceByte(jHenchman, "IsDM", 0); + // Rename them with a number added to the end, helps tell the + // difference between the PC and the new henchman. + string sNameIndex, sLastName = JsonGetString(GffGetLocString(jHenchman, "LastName")); + int nNameIndex = StringToInt(GetStringRight(sLastName, 1)); + if(nNameIndex > 0) GetStringLeft(sLastName, GetStringLength(sLastName) -1) + IntToString(nNameIndex++); + else sNameIndex = "_1"; + jHenchman = GffReplaceLocString(jHenchman, "LastName", sLastName + "_"); + } CheckHenchmanDataAndInitialize(oPC, sSlot); SetHenchmanDbString(oPC, "image", GetPortraitResRef(oHenchman), sSlot); SetHenchmanDbString(oPC, "henchname", sHenchmanName, sSlot); @@ -450,15 +462,13 @@ void SaveYourHenchman(object oPC, int nToken, string sParty) SetHenchmanDbString(oPC, "stats", sStats, sSlot); SetHenchmanDbString(oPC, "classes", sClasses, sSlot); SetHenchmanDbJson(oPC, "henchman", jHenchman, sSlot); - if(sName == "") ai_SendMessages(sHenchmanName + " has been saved to the party.", AI_COLOR_GREEN, oPC); - else ai_SendMessages(sHenchmanName + " has replaced a copy of themselves in the party.", AI_COLOR_GREEN, oPC); + if(sName == "") ai_SendMessages(sHenchmanName + " has been saved to the party " + sParty + ".", AI_COLOR_GREEN, oPC); + else ai_SendMessages(sHenchmanName + " has replaced a copy of themselves in the party " + sParty + ".", AI_COLOR_GREEN, oPC); break; } nIndex++; } -if(nIndex == nMaxHenchman) ai_SendMessages("This party is full!", AI_COLOR_RED, oPC); - NuiDestroy(oPC, nToken); - ExecuteScript("pi_henchmen", oPC); + if(nIndex == nMaxHenchman) ai_SendMessages("This party is full!", AI_COLOR_RED, oPC); } void SaveWholeParty(object oPC, int nToken, string sParty) { @@ -672,11 +682,8 @@ int GetSelectionByPackage2DA(string sClass, int nPackage) if(Get2DAString("packages", "ClassID", nIndex) == sClass) { sPackageName = GetStringByStrRef(StringToInt(Get2DAString("packages", "Name", nIndex))); - if(sPackageName != "Bad Strref" && sPackageName != "") - { - if(nPackage == nIndex) return nSelection; - nSelection++; - } + if(nPackage == nIndex) return nSelection; + nSelection++; } nIndex++; } @@ -814,10 +821,25 @@ json CreateLevelStatList(json jHenchman, object oHenchman, object oPC, int nLeve { jLevelArray = JsonArrayInsert(jLevelArray, jLevel); } - WriteTimestampedLogEntry("pinc_henchmen, 813, Adding LvlStatList to " + GetName(oHenchman)); + //WriteTimestampedLogEntry("pinc_henchmen, 813, Creating LvlStatList for " + GetName(oHenchman)); return GffAddList(jHenchman, "LvlStatList", jLevelArray); } -int CanSelectFeat(json jCreature, object oCreature, int nFeat, int nPosition = 1) +int GetHasJFeat(int nFeat, json jFeatList) +{ + int nIndex, nJFeat, nMaxFeats = JsonGetLength(jFeatList); + json jFeat; + //WriteTimestampedLogEntry("pinc_henchmen, 831, nFeat: " + IntToString(nFeat) + " nMaxFeats: " + IntToString(nMaxFeats) + "."); + while(nIndex < nMaxFeats) + { + jFeat = JsonArrayGet(jFeatList, nIndex); + nJFeat = JsonGetInt(GffGetWord(jFeat, "Feat")); + //WriteTimestampedLogEntry("pinc_henchmen, 831, nJFeat: " + IntToString(nJFeat) + "."); + if(nJFeat == nFeat) return TRUE; + nIndex++; + } + return FALSE; +} +int CanSelectFeat(json jCreature, object oCreature, int nFeat, json jFeats, int nPosition = 1) { // Check if all classes can use. int n2DAStat = StringToInt(Get2DAString("feat", "ALLCLASSESCANUSE", nFeat)); @@ -855,28 +877,30 @@ int CanSelectFeat(json jCreature, object oCreature, int nFeat, int nPosition = 1 n2DAStat = StringToInt(Get2DAString("feat", "MINSPELLLVL", nFeat)); int nSpellLevel = 0, nClass = GetClassByPosition(nPosition, oCreature); string s2DAName = Get2DAString("classes", "SpellGainTable", nClass); - int nLevel = GetLevelByPosition(nPosition, oCreature); if(s2DAName != "") { + int nLevel = GetLevelByPosition(nPosition, oCreature); nSpellLevel = StringToInt(Get2DAString(s2DAName, "NumSpellLevels", nLevel - 1)) - 1; + if(nSpellLevel < 0) nSpellLevel = 0; } if(nSpellLevel < n2DAStat) return FALSE; n2DAStat = StringToInt(Get2DAString("feat", "PREREQFEAT1", nFeat)); - if(n2DAStat > 0) + if(n2DAStat > 0 && GetHasJFeat(n2DAStat, jFeats)) { - // ************************************** Add code to search jCreature's feats! - if(!GetHasFeat(n2DAStat, oCreature)) return FALSE; n2DAStat = StringToInt(Get2DAString("feat", "PREREQFEAT2", nFeat)); - if(!GetHasFeat(n2DAStat, oCreature)) return FALSE; + if(n2DAStat > 0 && !GetHasJFeat(n2DAStat, jFeats)) return FALSE; } int nIndex; while(nIndex < 5) { n2DAStat = StringToInt(Get2DAString("feat", "OrReqFeat" + IntToString(nIndex), nFeat)); if(nIndex == 0 && n2DAStat == 0) break; - if(GetHasFeat(n2DAStat, oCreature)) break; - nIndex++; - if(nIndex == 5) return FALSE; + if(n2DAStat > 0) + { + if(GetHasJFeat(n2DAStat, jFeats)) break; + } + else return FALSE; + ++nIndex; } string s2DAStat = Get2DAString("feat", "REQSKILL", nFeat); if(s2DAStat != "") @@ -969,6 +993,7 @@ json ResetFeats(json jHenchman, object oHenchman) int nRace = GetRacialType(oHenchman); string sRace2DAName = Get2DAString("racialtypes", "FeatsTable", nRace); // Give racial feats. + WriteTimestampedLogEntry("pinc_henchmen, 996, Checking for racial feats."); int nRaceRow, nRaceFeat; int nRaceMaxRow = Get2DARowCount(sRace2DAName); while(nRaceRow < nRaceMaxRow) @@ -978,11 +1003,12 @@ json ResetFeats(json jHenchman, object oHenchman) jFeat = GffAddWord(jFeat, "Feat", nRaceFeat); jFeat = JsonObjectSet(jFeat, "__struct_id", JsonInt(1)); jFeatList = JsonArrayInsert(jFeatList, jFeat); - WriteTimestampedLogEntry("pinc_henchmen, 973, Adding racial feat: " + + WriteTimestampedLogEntry("pinc_henchmen, 999, Adding racial feat: " + Get2DAString("feat", "LABEL", nRaceFeat)); nRaceRow++; } // Give class feats. + WriteTimestampedLogEntry("pinc_henchmen, 1004, Checking for class feats."); int nClass = GetClassByPosition(1, oHenchman); string sGranted, sList; string sClsFeat2DAName = Get2DAString("classes", "FeatsTable", nClass); @@ -1000,7 +1026,7 @@ json ResetFeats(json jHenchman, object oHenchman) jFeat = GffAddWord(jFeat, "Feat", nClassFeat); jFeat = JsonObjectSet(jFeat, "__struct_id", JsonInt(1)); jFeatList = JsonArrayInsert(jFeatList, jFeat); - WriteTimestampedLogEntry("pinc_henchmen, 995, Adding class feat: " + + WriteTimestampedLogEntry("pinc_henchmen, 1022, Adding class feat: " + Get2DAString("feat", "LABEL", nClassFeat)); } } @@ -1010,6 +1036,7 @@ json ResetFeats(json jHenchman, object oHenchman) int nPackageFeat, nPackageRow; string sBonusFeat2DAName = Get2DAString("classes", "BonusFeatsTable", nClass); int nNumOfFeats = StringToInt(Get2DAString(sBonusFeat2DAName, "Bonus", nLevel)); + WriteTimestampedLogEntry("pinc_henchmen, 1032, Select " + IntToString(nNumOfFeats) + " bonus feats."); string sPackage2DAName = Get2DAString("packages", "FeatPref2DA", nClass); int nPackageMaxRow = Get2DARowCount(sPackage2DAName); // Give bonus feats based on the package. @@ -1026,13 +1053,13 @@ json ResetFeats(json jHenchman, object oHenchman) if(nClassFeat == nPackageFeat) { sList = Get2DAString(sClsFeat2DAName, "List", nClassRow); - if((sList == "1" || sList == "2") && CanSelectFeat(jHenchman, oHenchman, nClassFeat)) + if((sList == "1" || sList == "2") && CanSelectFeat(jHenchman, oHenchman, nClassFeat, jFeatList)) { jFeat = JsonObject(); jFeat = GffAddWord(jFeat, "Feat", nClassFeat); jFeat = JsonObjectSet(jFeat, "__struct_id", JsonInt(1)); jFeatList = JsonArrayInsert(jFeatList, jFeat); - WriteTimestampedLogEntry("pinc_henchmen, 1028, Adding class bonus feat: " + + WriteTimestampedLogEntry("pinc_henchmen, 1055, Adding class bonus feat: " + Get2DAString("feat", "LABEL", nPackageFeat)); nNumOfFeats--; } @@ -1046,39 +1073,44 @@ json ResetFeats(json jHenchman, object oHenchman) // Give picked feats from package. nNumOfFeats = 1; if(GetHasFeat(FEAT_QUICK_TO_MASTER, oHenchman)) nNumOfFeats++; + WriteTimestampedLogEntry("pinc_henchmen, 1069, Select " + IntToString(nNumOfFeats) + " feats for character."); nPackageRow = 0; while(nPackageRow < nPackageMaxRow) { nClassRow = 0; nPackageFeat = StringToInt(Get2DAString(sPackage2DAName, "FeatIndex", nPackageRow)); - if(CanSelectFeat(jHenchman, oHenchman, nPackageFeat)) + //WriteTimestampedLogEntry("pinc_henchmen, 1075, nPackageFeat: " + Get2DAString("feat", "LABEL", nPackageFeat) + "."); + if(CanSelectFeat(jHenchman, oHenchman, nPackageFeat, jFeatList)) { jFeat = JsonObject(); jFeat = GffAddWord(jFeat, "Feat", nPackageFeat); jFeat = JsonObjectSet(jFeat, "__struct_id", JsonInt(1)); jFeatList = JsonArrayInsert(jFeatList, jFeat); - WriteTimestampedLogEntry("pinc_henchmen, 1053, Adding character bonus feat: " + + WriteTimestampedLogEntry("pinc_henchmen, 1082, Selecting character feat: " + Get2DAString("feat", "LABEL", nPackageFeat)); nNumOfFeats--; } if(nNumOfFeats < 1) break; nPackageRow++; } + WriteTimestampedLogEntry("pinc_henchmen, 1089, Adding feat list."); jHenchman = GffReplaceList(jHenchman, "FeatList", jFeatList); return jHenchman; } -json ResetSkills(json jHenchman, object oHenchman) +json ResetSkills(json jHenchman, object oHenchman, int nLevel) { // We remake the Skill List if the character doesn't have a level list! int nClass = GetClassByPosition(1, oHenchman); int nSkillPoints, nIntMod = GetAbilityModifier(ABILITY_INTELLIGENCE, oHenchman); - if(nIntMod > 0) nSkillPoints = nIntMod * 4; - if(GetRacialType(oHenchman) == RACIAL_TYPE_HUMAN) nSkillPoints += 4; - nSkillPoints += StringToInt(Get2DAString("classes", "SkillPointBase", nClass)) * 4; - int nMaxRanks = 5; + if(nIntMod > 0) nSkillPoints = nIntMod; + if(GetRacialType(oHenchman) == RACIAL_TYPE_HUMAN) nSkillPoints += 1; + nSkillPoints += StringToInt(Get2DAString("classes", "SkillPointBase", nClass)); + nSkillPoints = nSkillPoints * (nLevel + 3); + int nMaxRanks = 3 + nLevel; json jSkillList = JsonArray(); json jSkill; // Setup the Skill List. + WriteTimestampedLogEntry("pinc_henchmen, 1112, Generating skill list."); int nIndex, nSkillMaxRow = Get2DARowCount("skills"); for(nIndex = 0; nIndex < nSkillMaxRow; nIndex++) { @@ -1088,6 +1120,7 @@ json ResetSkills(json jHenchman, object oHenchman) jSkillList = JsonArrayInsert(jSkillList, jSkill); } // Give skill points based on the package. + WriteTimestampedLogEntry("pinc_henchmen, 1116, Gets " + IntToString(nSkillPoints) + " skill points."); int nPackageSkill, nPackageRow, nCurrentRanks, bCrossClass, nClassRow, nNewRanks; string sPackage2DAName = Get2DAString("packages", "SkillPref2DA", nClass); int nPackageMaxRow = Get2DARowCount(sPackage2DAName); @@ -1116,8 +1149,9 @@ json ResetSkills(json jHenchman, object oHenchman) { jSkill = GffReplaceByte(jSkill, "Rank", nCurrentRanks + nNewRanks); jSkillList = JsonArraySet(jSkillList, nPackageSkill, jSkill); - WriteTimestampedLogEntry("pinc_henchmen, 1110, Adding " + IntToString(nNewRanks) + - " ranks to " + Get2DAString("skills", "Label", nPackageSkill)); + WriteTimestampedLogEntry("pinc_henchmen, 1145, Adding " + IntToString(nNewRanks) + + " ranks to " + Get2DAString("skills", "Label", nPackageSkill) + + " CrossClass: " + IntToString(bCrossClass)); nSkillPoints -= nNewRanks; } nPackageRow++; @@ -1127,7 +1161,9 @@ json ResetSkills(json jHenchman, object oHenchman) } json ResetSpellsKnown(json jClass, object oHenchman) { + WriteTimestampedLogEntry("pinc_henchmen, 1157, Checking for spells known."); int nClass = GetClassByPosition(1, oHenchman); + WriteTimestampedLogEntry("pinc_henchmen, 1159, SpellCaster: " + Get2DAString("classes", "SpellCaster", nClass)); if(Get2DAString("classes", "SpellCaster", nClass) == "0") return jClass; int nLevel = 0; // We remake the Known spell list if the character doesn't have a level list! @@ -1169,7 +1205,7 @@ json ResetSpellsKnown(json jClass, object oHenchman) { nSpellsKnown = StringToInt(Get2DAString(sSpellKnown2DAName, "SpellLevel" + sSpellLevel, nLevel)); } - WriteTimestampedLogEntry("pinc_henchmen, 1165, nSpellsKnown: " + IntToString(nSpellsKnown)); + WriteTimestampedLogEntry("pinc_henchmen, 1201, nSpellsKnown: " + IntToString(nSpellsKnown)); jKnownList = JsonArray(); nPackageRow = 0; while(nPackageRow < nPackageMaxRow && nSpellsKnown > 0) @@ -1191,7 +1227,7 @@ json ResetSpellsKnown(json jClass, object oHenchman) if(JsonGetLength(jKnownList) == 0) { jClass = GffRemoveList(jClass, "KnownList" + sSpellLevel); - WriteTimestampedLogEntry("pinc_henchmen, 1187, Removing KnownList" + sSpellLevel); + WriteTimestampedLogEntry("pinc_henchmen, 1223, Removing KnownList" + sSpellLevel); } else if(JsonGetType(GffGetList(jClass, "KnownList" + sSpellLevel)) != JSON_TYPE_NULL) { @@ -1207,7 +1243,7 @@ json ResetSpellsKnown(json jClass, object oHenchman) if(JsonGetType(jKnownList) != JSON_TYPE_NULL) { jClass = GffRemoveList(jClass, "KnownList" + sSpellLevel); - WriteTimestampedLogEntry("pinc_henchmen, 1203, Removing KnownList" + sSpellLevel); + WriteTimestampedLogEntry("pinc_henchmen, 1239, Removing KnownList" + sSpellLevel); } } if(bMemorizesSpells) @@ -1216,7 +1252,7 @@ json ResetSpellsKnown(json jClass, object oHenchman) if(JsonGetType(jMemorizedList) != JSON_TYPE_NULL) { jClass = GffRemoveList(jClass, "MemorizedList" + sSpellLevel); - WriteTimestampedLogEntry("pinc_henchmen, 1210, Removing MemorizedList" + sSpellLevel); + WriteTimestampedLogEntry("pinc_henchmen, 1248, Removing MemorizedList" + sSpellLevel); } } else @@ -1227,7 +1263,7 @@ json ResetSpellsKnown(json jClass, object oHenchman) jSpell = GffReplaceByte(jSpell, "NumSpellsLeft", nSpellsKnown); jSpellsPerDayList = JsonArraySet(jSpellsPerDayList, nSpellLevel, jSpell); jClass = GffReplaceList(jClass, "SpellsPerDayList", jSpellsPerDayList); - WriteTimestampedLogEntry("pinc_henchmen, 1223, Setting SpellsPerDay to " + + WriteTimestampedLogEntry("pinc_henchmen, 1259, Setting SpellsPerDay to " + IntToString(nSpellsKnown)); } nSpellLevel++; @@ -1238,11 +1274,12 @@ object ResetCharacter(object oPC, object oHenchman) { SetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE", TRUE); RemoveHenchman(oPC, oHenchman); + ChangeToStandardFaction(oHenchman, STANDARD_FACTION_DEFENDER); json jHenchman = ObjectToJson(oHenchman, TRUE); json jClassList = GffGetList(jHenchman, "ClassList"); json jClass = JsonArrayGet(jClassList, 0); // Set the Class list to the first class only and put at level 1. - int nClass = JsonGetInt(JsonObjectGet(jClass, "Class")); + int nClass = JsonGetInt(GffGetInt(jClass, "Class")); jClass = GffReplaceShort(jClass, "ClassLevel", 1); // Delete extra classes. int nClassIndex = JsonGetLength(jClassList) - 1; @@ -1250,15 +1287,10 @@ object ResetCharacter(object oPC, object oHenchman) { jClassList = JsonArrayDel(jClassList, nClassIndex--); } - int nHitPoints = StringToInt(Get2DAString("classes", "HitDie", nClass)); - int nMod = JsonGetInt(GffGetByte(jHenchman, "Con")); - if(nMod > 9) nHitPoints += (nMod - 10) / 2; - else nHitPoints += (nMod - 11) / 2; - jHenchman = GffReplaceShort(jHenchman, "CurrentHitPoints", nHitPoints); - jHenchman = GffReplaceShort(jHenchman, "HitPoints", nHitPoints); - jHenchman = GffReplaceShort(jHenchman, "MaxHitPoints", nHitPoints); jHenchman = GffReplaceDword(jHenchman, "Experience", 0); jHenchman = GffReplaceFloat(jHenchman, "ChallengeRating", 1.0); +// int nPackage = GetLocalInt(oHenchman, "PACKAGE_SELECTED_1"); +// if(nPackage) jHenchman = GffReplaceByte(jHenchman, "StartingPackage", nPackage); string s2DA = Get2DAString("classes", "AttackBonusTable", nClass); int nAtk = StringToInt(Get2DAString(s2DA, "BAB", 0)); jHenchman = GffReplaceByte(jHenchman, "BaseAttackBonus", nAtk); @@ -1272,7 +1304,7 @@ object ResetCharacter(object oPC, object oHenchman) json jLvlStatList = GffGetList(jHenchman, "LvlStatList"); if(JsonGetType(jLvlStatList) != JSON_TYPE_NULL) { - WriteTimestampedLogEntry("pinc_henchmen 1275, jLvlStatList: " + JsonDump(jLvlStatList, 4)); + //WriteTimestampedLogEntry("pinc_henchmen 1300, jLvlStatList: " + JsonDump(jLvlStatList, 4)); int nLevel = 1, nLevelTrack = 1; int nAbilityStatIncrease, nAbility; string sAbility; @@ -1280,7 +1312,7 @@ object ResetCharacter(object oPC, object oHenchman) json jLevel = JsonArrayGet(jLvlStatList, nLevel); while(JsonGetType(jLevel) != JSON_TYPE_NULL) { - WriteTimestampedLogEntry("inc_henchmen, 1297, Checking level " + IntToString(nLevelTrack)); + WriteTimestampedLogEntry("inc_henchmen, 1308, Checking level " + IntToString(nLevelTrack)); // Remove all Ability score increases for each level from ability scores. jAbility = GffGetByte(jLevel, "LvlStatAbility"); if(JsonGetType(jAbility) != JSON_TYPE_NULL) @@ -1306,12 +1338,21 @@ object ResetCharacter(object oPC, object oHenchman) jHenchman = GffRemoveList(jHenchman, "LvlStatList"); } jHenchman = CreateLevelStatList(jHenchman, oHenchman, oPC, 1); - jHenchman = ResetSkills(jHenchman, oHenchman); + int nHitPoints = StringToInt(Get2DAString("classes", "HitDie", nClass)); + int nConstitution = JsonGetInt(GffGetByte(jHenchman, "Con")); + int nRace = JsonGetInt(GffGetByte(jHenchman, "Race")); + nConstitution += StringToInt(Get2DAString("racialtypes", "ConAdjust", nRace)); + if(nConstitution > 9) nHitPoints += (nConstitution - 10) / 2; + else nHitPoints += (nConstitution - 11) / 2; + jHenchman = GffReplaceShort(jHenchman, "CurrentHitPoints", nHitPoints); + jHenchman = GffReplaceShort(jHenchman, "HitPoints", nHitPoints); + jHenchman = GffReplaceShort(jHenchman, "MaxHitPoints", nHitPoints); + jHenchman = ResetSkills(jHenchman, oHenchman, 1); jHenchman = ResetFeats(jHenchman, oHenchman); jClass = ResetSpellsKnown(jClass, oHenchman); jClassList = JsonArraySet(jClassList, 0, jClass); jHenchman = GffReplaceList(jHenchman, "ClassList", jClassList); - //WriteTimestampedLogEntry("pinc_henchmen 1397, jHenchman: " + JsonDump(jHenchman, 4)); + //WriteTimestampedLogEntry("pinc_henchmen 1348, jHenchman: " + JsonDump(jHenchman, 4)); location lLocation = GetLocation(oHenchman); int nFamiliar, nCompanion; object oCompanion = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oHenchman); @@ -1332,13 +1373,13 @@ void CreateCharacterEditGUIPanel(object oPC, object oHenchman) // Group 1 (Portrait)******************************************************* 151 / 73 // Group 1 Row 1 *********************************************************** 350 / 91 json jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); - jGroupRow = CreateTextEditBox (jGroupRow, "name_placeholder", "char_name", 15, FALSE, 140.0, 20.0); + jGroupRow = CreateTextEditBox (jGroupRow, "name_placeholder", "char_name", 50, FALSE, 140.0, 20.0); jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); // Add the group row to the group column. json jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); // Group 1 Row 1 *********************************************************** 350 / 91 jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); - jGroupRow = CreateTextEditBox (jGroupRow, "port_placeholder", "port_name", 15, FALSE, 140.0, 20.0, "port_tooltip"); + jGroupRow = CreateTextEditBox (jGroupRow, "port_placeholder", "port_name", 16, FALSE, 140.0, 20.0, "port_tooltip"); jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); // Add the group row to the group column. jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); @@ -1493,11 +1534,13 @@ void CreateCharacterEditGUIPanel(object oPC, object oHenchman) NuiSetBindWatch(oPC, nToken, "cmb_class_selected", bNoClass); NuiSetBind(oPC, nToken, "cmb_class_event", JsonBool(bNoClass)); int nPackage = GetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nClassOption + 1)); + //SendMessageToPC(oPC, "nPackage: " + IntToString(nPackage) + " nSelection: " + IntToString(GetSelectionByPackage2DA(sClass, nPackage))); if(nPackage == 0) { nPackage = GetPackageBySelection2DA(sClass, 0); SetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nClassOption + 1), nPackage); } + //SendMessageToPC(oPC, "nPackage: " + IntToString(nPackage) + " sClass: " + sClass); NuiSetBind(oPC, nToken, "cmb_package_selected", JsonInt(GetSelectionByPackage2DA(sClass, nPackage))); NuiSetBindWatch(oPC, nToken, "cmb_package_selected", bNoClass); NuiSetBind(oPC, nToken, "cmb_package_event", JsonBool(bNoClass)); @@ -1528,4 +1571,3 @@ void CreateCharacterDescriptionNUI(object oPC, string sName, string sIcon, strin // Row 2 NuiSetBind(oPC, nToken, "btn_ok_event", JsonBool(TRUE)); } -