diff --git a/_module/ifo/module.ifo.json b/_module/ifo/module.ifo.json index f79ac8f7..50cbc0e9 100644 --- a/_module/ifo/module.ifo.json +++ b/_module/ifo/module.ifo.json @@ -4351,7 +4351,7 @@ "Mod_Description": { "type": "cexolocstring", "value": { - "0": "Aantioch [PRC-CEP3]\nBy: DM McDaggart / PRC'd by: Jaysyn\n\n400+ Custom creatures.\n1100+ Items available.\n601 Areas and more to add.\n42 Unique merchants.\n\nThis server is in active development and could go down arbitrarily. It should be back up quickly in most cases." + "0": "Aantioch [PRC-CEP3]\nBy: DM McDaggart / PRC'd by: Jaysyn\n\n400+ Custom creatures.\n1100+ Items available.\n601 Areas and more to add.\n42 Unique merchants.\n\nNOW WITH PEPS AI!\n\nThis server is in active development and could go down arbitrarily. It should be back up quickly in most cases." } }, "Mod_DuskHour": { @@ -4393,6 +4393,13 @@ "Mod_HakList": { "type": "list", "value": [ + { + "__struct_id": 8, + "Mod_Hak": { + "type": "cexostring", + "value": "peps_prc8" + } + }, { "__struct_id": 8, "Mod_Hak": { diff --git a/_module/ncs/062_npc_say.ncs b/_module/ncs/062_npc_say.ncs index ebf24d78..a0d6633e 100644 Binary files a/_module/ncs/062_npc_say.ncs and b/_module/ncs/062_npc_say.ncs differ diff --git a/_module/ncs/0c_assoc_actions.ncs b/_module/ncs/0c_assoc_actions.ncs new file mode 100644 index 00000000..d469987d Binary files /dev/null and b/_module/ncs/0c_assoc_actions.ncs differ diff --git a/_module/ncs/0c_cast_polymorp.ncs b/_module/ncs/0c_cast_polymorp.ncs new file mode 100644 index 00000000..9f1916e4 Binary files /dev/null and b/_module/ncs/0c_cast_polymorp.ncs differ diff --git a/_module/ncs/0c_fire_henchmen.ncs b/_module/ncs/0c_fire_henchmen.ncs new file mode 100644 index 00000000..800bd810 Binary files /dev/null and b/_module/ncs/0c_fire_henchmen.ncs differ diff --git a/_module/ncs/0c_get_convo.ncs b/_module/ncs/0c_get_convo.ncs new file mode 100644 index 00000000..fd990929 Binary files /dev/null and b/_module/ncs/0c_get_convo.ncs differ diff --git a/_module/ncs/0c_get_henchman.ncs b/_module/ncs/0c_get_henchman.ncs new file mode 100644 index 00000000..13e4af79 Binary files /dev/null and b/_module/ncs/0c_get_henchman.ncs differ diff --git a/_module/ncs/0c_h_cast_spell.ncs b/_module/ncs/0c_h_cast_spell.ncs new file mode 100644 index 00000000..4a5f698c Binary files /dev/null and b/_module/ncs/0c_h_cast_spell.ncs differ diff --git a/_module/ncs/0c_henchmenspell.ncs b/_module/ncs/0c_henchmenspell.ncs new file mode 100644 index 00000000..751cb571 Binary files /dev/null and b/_module/ncs/0c_henchmenspell.ncs differ diff --git a/_module/ncs/0c_if_a_magic_m.ncs b/_module/ncs/0c_if_a_magic_m.ncs new file mode 100644 index 00000000..3b61ec9d Binary files /dev/null and b/_module/ncs/0c_if_a_magic_m.ncs differ diff --git a/_module/ncs/0c_if_ass_convo.ncs b/_module/ncs/0c_if_ass_convo.ncs new file mode 100644 index 00000000..630c0c8f Binary files /dev/null and b/_module/ncs/0c_if_ass_convo.ncs differ diff --git a/_module/ncs/0c_if_assoc_mode.ncs b/_module/ncs/0c_if_assoc_mode.ncs new file mode 100644 index 00000000..172c75d3 Binary files /dev/null and b/_module/ncs/0c_if_assoc_mode.ncs differ diff --git a/_module/ncs/0c_if_cntrspell.ncs b/_module/ncs/0c_if_cntrspell.ncs new file mode 100644 index 00000000..4667f688 Binary files /dev/null and b/_module/ncs/0c_if_cntrspell.ncs differ diff --git a/_module/ncs/0c_if_com_script.ncs b/_module/ncs/0c_if_com_script.ncs new file mode 100644 index 00000000..eb6dcb21 Binary files /dev/null and b/_module/ncs/0c_if_com_script.ncs differ diff --git a/_module/ncs/0c_if_convo.ncs b/_module/ncs/0c_if_convo.ncs new file mode 100644 index 00000000..13b85f09 Binary files /dev/null and b/_module/ncs/0c_if_convo.ncs differ diff --git a/_module/ncs/0c_if_has_assoc.ncs b/_module/ncs/0c_if_has_assoc.ncs new file mode 100644 index 00000000..ffdfb4f3 Binary files /dev/null and b/_module/ncs/0c_if_has_assoc.ncs differ diff --git a/_module/ncs/0c_if_has_class.ncs b/_module/ncs/0c_if_has_class.ncs new file mode 100644 index 00000000..1461b033 Binary files /dev/null and b/_module/ncs/0c_if_has_class.ncs differ diff --git a/_module/ncs/0c_if_has_feat.ncs b/_module/ncs/0c_if_has_feat.ncs new file mode 100644 index 00000000..1db2eecd Binary files /dev/null and b/_module/ncs/0c_if_has_feat.ncs differ diff --git a/_module/ncs/0c_if_has_spell.ncs b/_module/ncs/0c_if_has_spell.ncs new file mode 100644 index 00000000..aa89e0e0 Binary files /dev/null and b/_module/ncs/0c_if_has_spell.ncs differ diff --git a/_module/ncs/0c_if_hen_leave.ncs b/_module/ncs/0c_if_hen_leave.ncs new file mode 100644 index 00000000..be810642 Binary files /dev/null and b/_module/ncs/0c_if_hen_leave.ncs differ diff --git a/_module/ncs/0c_if_identify.ncs b/_module/ncs/0c_if_identify.ncs new file mode 100644 index 00000000..af035767 Binary files /dev/null and b/_module/ncs/0c_if_identify.ncs differ diff --git a/_module/ncs/0c_if_not_master.ncs b/_module/ncs/0c_if_not_master.ncs new file mode 100644 index 00000000..4af03a6f Binary files /dev/null and b/_module/ncs/0c_if_not_master.ncs differ diff --git a/_module/ncs/0c_if_open_inven.ncs b/_module/ncs/0c_if_open_inven.ncs new file mode 100644 index 00000000..c51df655 Binary files /dev/null and b/_module/ncs/0c_if_open_inven.ncs differ diff --git a/_module/ncs/0c_if_pickuploot.ncs b/_module/ncs/0c_if_pickuploot.ncs new file mode 100644 index 00000000..5ec22e57 Binary files /dev/null and b/_module/ncs/0c_if_pickuploot.ncs differ diff --git a/_module/ncs/0c_if_polymorph.ncs b/_module/ncs/0c_if_polymorph.ncs new file mode 100644 index 00000000..0778730e Binary files /dev/null and b/_module/ncs/0c_if_polymorph.ncs differ diff --git a/_module/ncs/0c_if_scout.ncs b/_module/ncs/0c_if_scout.ncs new file mode 100644 index 00000000..5ec22e57 Binary files /dev/null and b/_module/ncs/0c_if_scout.ncs differ diff --git a/_module/ncs/0c_if_skillrank.ncs b/_module/ncs/0c_if_skillrank.ncs new file mode 100644 index 00000000..0116caa1 Binary files /dev/null and b/_module/ncs/0c_if_skillrank.ncs differ diff --git a/_module/ncs/0c_if_taunt.ncs b/_module/ncs/0c_if_taunt.ncs new file mode 100644 index 00000000..342a27a5 Binary files /dev/null and b/_module/ncs/0c_if_taunt.ncs differ diff --git a/_module/ncs/0c_listhenchman.ncs b/_module/ncs/0c_listhenchman.ncs new file mode 100644 index 00000000..de1e30e0 Binary files /dev/null and b/_module/ncs/0c_listhenchman.ncs differ diff --git a/_module/ncs/0c_no_com_script.ncs b/_module/ncs/0c_no_com_script.ncs new file mode 100644 index 00000000..65232c21 Binary files /dev/null and b/_module/ncs/0c_no_com_script.ncs differ diff --git a/_module/ncs/0c_remove_effect.ncs b/_module/ncs/0c_remove_effect.ncs new file mode 100644 index 00000000..f525d1a8 Binary files /dev/null and b/_module/ncs/0c_remove_effect.ncs differ diff --git a/_module/ncs/0c_summon_assoc.ncs b/_module/ncs/0c_summon_assoc.ncs new file mode 100644 index 00000000..86796be8 Binary files /dev/null and b/_module/ncs/0c_summon_assoc.ncs differ diff --git a/_module/ncs/0c_use_feat.ncs b/_module/ncs/0c_use_feat.ncs new file mode 100644 index 00000000..910277c8 Binary files /dev/null and b/_module/ncs/0c_use_feat.ncs differ diff --git a/_module/ncs/0e_c2_1_hb.ncs b/_module/ncs/0e_c2_1_hb.ncs new file mode 100644 index 00000000..43d1d40d Binary files /dev/null and b/_module/ncs/0e_c2_1_hb.ncs differ diff --git a/_module/ncs/0e_c2_7_ondeath.ncs b/_module/ncs/0e_c2_7_ondeath.ncs new file mode 100644 index 00000000..7705e5ff Binary files /dev/null and b/_module/ncs/0e_c2_7_ondeath.ncs differ diff --git a/_module/ncs/0e_ch_1_hb.ncs b/_module/ncs/0e_ch_1_hb.ncs new file mode 100644 index 00000000..60b5196a Binary files /dev/null and b/_module/ncs/0e_ch_1_hb.ncs differ diff --git a/_module/ncs/0e_ch_7_ondeath.ncs b/_module/ncs/0e_ch_7_ondeath.ncs new file mode 100644 index 00000000..ca47b898 Binary files /dev/null and b/_module/ncs/0e_ch_7_ondeath.ncs differ diff --git a/_module/ncs/0e_do_combat_rnd.ncs b/_module/ncs/0e_do_combat_rnd.ncs new file mode 100644 index 00000000..735f4df5 Binary files /dev/null and b/_module/ncs/0e_do_combat_rnd.ncs differ diff --git a/_module/ncs/0e_gui_events.ncs b/_module/ncs/0e_gui_events.ncs new file mode 100644 index 00000000..24c02d6b Binary files /dev/null and b/_module/ncs/0e_gui_events.ncs differ diff --git a/_module/ncs/0e_id_events.ncs b/_module/ncs/0e_id_events.ncs new file mode 100644 index 00000000..48e25c79 Binary files /dev/null and b/_module/ncs/0e_id_events.ncs differ diff --git a/_module/ncs/0e_nui.ncs b/_module/ncs/0e_nui.ncs new file mode 100644 index 00000000..13bdf7a4 Binary files /dev/null and b/_module/ncs/0e_nui.ncs differ diff --git a/_module/ncs/0e_nui_dm.ncs b/_module/ncs/0e_nui_dm.ncs new file mode 100644 index 00000000..d2f8a0e2 Binary files /dev/null and b/_module/ncs/0e_nui_dm.ncs differ diff --git a/_module/ncs/0e_onclientload.ncs b/_module/ncs/0e_onclientload.ncs new file mode 100644 index 00000000..fac6ad0d Binary files /dev/null and b/_module/ncs/0e_onclientload.ncs differ diff --git a/_module/ncs/0e_player_target.ncs b/_module/ncs/0e_player_target.ncs new file mode 100644 index 00000000..48d8e031 Binary files /dev/null and b/_module/ncs/0e_player_target.ncs differ diff --git a/_module/ncs/0e_prc_ch_events.ncs b/_module/ncs/0e_prc_ch_events.ncs new file mode 100644 index 00000000..a21d7478 Binary files /dev/null and b/_module/ncs/0e_prc_ch_events.ncs differ diff --git a/_module/ncs/69_hen_combat.ncs b/_module/ncs/69_hen_combat.ncs index 498d28c2..3bb53189 100644 Binary files a/_module/ncs/69_hen_combat.ncs and b/_module/ncs/69_hen_combat.ncs differ diff --git a/_module/ncs/69_hen_conv.ncs b/_module/ncs/69_hen_conv.ncs index af9f8029..feb68195 100644 Binary files a/_module/ncs/69_hen_conv.ncs and b/_module/ncs/69_hen_conv.ncs differ diff --git a/_module/ncs/69_hen_death.ncs b/_module/ncs/69_hen_death.ncs index c9edcb94..b5fcb4ff 100644 Binary files a/_module/ncs/69_hen_death.ncs and b/_module/ncs/69_hen_death.ncs differ diff --git a/_module/ncs/69_hen_percep.ncs b/_module/ncs/69_hen_percep.ncs index 1347e97d..bfc23c37 100644 Binary files a/_module/ncs/69_hen_percep.ncs and b/_module/ncs/69_hen_percep.ncs differ diff --git a/_module/ncs/69_hen_spawnin.ncs b/_module/ncs/69_hen_spawnin.ncs index 8e9c3268..eea07289 100644 Binary files a/_module/ncs/69_hen_spawnin.ncs and b/_module/ncs/69_hen_spawnin.ncs differ diff --git a/_module/ncs/69_hen_spawnin2.ncs b/_module/ncs/69_hen_spawnin2.ncs index 8f894a03..973a69c2 100644 Binary files a/_module/ncs/69_hen_spawnin2.ncs and b/_module/ncs/69_hen_spawnin2.ncs differ diff --git a/_module/ncs/69_hench_cancast.ncs b/_module/ncs/69_hench_cancast.ncs index 5aa127c6..27581b4e 100644 Binary files a/_module/ncs/69_hench_cancast.ncs and b/_module/ncs/69_hench_cancast.ncs differ diff --git a/_module/ncs/69_hench_canlvl.ncs b/_module/ncs/69_hench_canlvl.ncs index d1e1aab5..e96b7a5f 100644 Binary files a/_module/ncs/69_hench_canlvl.ncs and b/_module/ncs/69_hench_canlvl.ncs differ diff --git a/_module/ncs/69_hench_canwork.ncs b/_module/ncs/69_hench_canwork.ncs index db7b2c5a..0d9ebe95 100644 Binary files a/_module/ncs/69_hench_canwork.ncs and b/_module/ncs/69_hench_canwork.ncs differ diff --git a/_module/ncs/69_hench_crtrno1.ncs b/_module/ncs/69_hench_crtrno1.ncs index 56f6bcec..1f457c42 100644 Binary files a/_module/ncs/69_hench_crtrno1.ncs and b/_module/ncs/69_hench_crtrno1.ncs differ diff --git a/_module/ncs/69_hench_crtrno2.ncs b/_module/ncs/69_hench_crtrno2.ncs index 4d48b469..78e6015a 100644 Binary files a/_module/ncs/69_hench_crtrno2.ncs and b/_module/ncs/69_hench_crtrno2.ncs differ diff --git a/_module/ncs/69_hench_crtrsmn.ncs b/_module/ncs/69_hench_crtrsmn.ncs index 8d9ab23a..8a8d637b 100644 Binary files a/_module/ncs/69_hench_crtrsmn.ncs and b/_module/ncs/69_hench_crtrsmn.ncs differ diff --git a/_module/ncs/69_hench_crtruns.ncs b/_module/ncs/69_hench_crtruns.ncs index 69ccf662..0c4fb143 100644 Binary files a/_module/ncs/69_hench_crtruns.ncs and b/_module/ncs/69_hench_crtruns.ncs differ diff --git a/_module/ncs/69_hench_crtrye1.ncs b/_module/ncs/69_hench_crtrye1.ncs index b8f5c493..638d6b62 100644 Binary files a/_module/ncs/69_hench_crtrye1.ncs and b/_module/ncs/69_hench_crtrye1.ncs differ diff --git a/_module/ncs/69_hench_crtrye2.ncs b/_module/ncs/69_hench_crtrye2.ncs index 23960992..a8bd7b06 100644 Binary files a/_module/ncs/69_hench_crtrye2.ncs and b/_module/ncs/69_hench_crtrye2.ncs differ diff --git a/_module/ncs/69_hench_destobj.ncs b/_module/ncs/69_hench_destobj.ncs index ace583d5..6cd0839e 100644 Binary files a/_module/ncs/69_hench_destobj.ncs and b/_module/ncs/69_hench_destobj.ncs differ diff --git a/_module/ncs/69_hench_didie.ncs b/_module/ncs/69_hench_didie.ncs index 3a1ef5e7..39648e50 100644 Binary files a/_module/ncs/69_hench_didie.ncs and b/_module/ncs/69_hench_didie.ncs differ diff --git a/_module/ncs/69_hench_eqmelee.ncs b/_module/ncs/69_hench_eqmelee.ncs index ac1d951c..1e32d87e 100644 Binary files a/_module/ncs/69_hench_eqmelee.ncs and b/_module/ncs/69_hench_eqmelee.ncs differ diff --git a/_module/ncs/69_hench_eqrange.ncs b/_module/ncs/69_hench_eqrange.ncs index 8dd69f3f..39c13195 100644 Binary files a/_module/ncs/69_hench_eqrange.ncs and b/_module/ncs/69_hench_eqrange.ncs differ diff --git a/_module/ncs/69_hench_fire.ncs b/_module/ncs/69_hench_fire.ncs index 1d47d7a1..f9c10ce8 100644 Binary files a/_module/ncs/69_hench_fire.ncs and b/_module/ncs/69_hench_fire.ncs differ diff --git a/_module/ncs/69_hench_fired.ncs b/_module/ncs/69_hench_fired.ncs index 0c843a5a..cf5651c0 100644 Binary files a/_module/ncs/69_hench_fired.ncs and b/_module/ncs/69_hench_fired.ncs differ diff --git a/_module/ncs/69_hench_gomelee.ncs b/_module/ncs/69_hench_gomelee.ncs index c6edfcaa..5e72bd78 100644 Binary files a/_module/ncs/69_hench_gomelee.ncs and b/_module/ncs/69_hench_gomelee.ncs differ diff --git a/_module/ncs/69_hench_gorange.ncs b/_module/ncs/69_hench_gorange.ncs index a7ebb2e2..d20f77cd 100644 Binary files a/_module/ncs/69_hench_gorange.ncs and b/_module/ncs/69_hench_gorange.ncs differ diff --git a/_module/ncs/69_hench_hasdual.ncs b/_module/ncs/69_hench_hasdual.ncs index 167c984b..c447d261 100644 Binary files a/_module/ncs/69_hench_hasdual.ncs and b/_module/ncs/69_hench_hasdual.ncs differ diff --git a/_module/ncs/69_hench_heall.ncs b/_module/ncs/69_hench_heall.ncs index 14442190..c95ba9ed 100644 Binary files a/_module/ncs/69_hench_heall.ncs and b/_module/ncs/69_hench_heall.ncs differ diff --git a/_module/ncs/69_hench_henchxp.ncs b/_module/ncs/69_hench_henchxp.ncs index f5711460..ea1c41bf 100644 Binary files a/_module/ncs/69_hench_henchxp.ncs and b/_module/ncs/69_hench_henchxp.ncs differ diff --git a/_module/ncs/69_hench_hire.ncs b/_module/ncs/69_hench_hire.ncs index 7d57a7dd..2dd4eb40 100644 Binary files a/_module/ncs/69_hench_hire.ncs and b/_module/ncs/69_hench_hire.ncs differ diff --git a/_module/ncs/69_hench_identfy.ncs b/_module/ncs/69_hench_identfy.ncs index 576f9869..2ea70a30 100644 Binary files a/_module/ncs/69_hench_identfy.ncs and b/_module/ncs/69_hench_identfy.ncs differ diff --git a/_module/ncs/69_hench_level.ncs b/_module/ncs/69_hench_level.ncs index 9d4fc0dc..bca685be 100644 Binary files a/_module/ncs/69_hench_level.ncs and b/_module/ncs/69_hench_level.ncs differ diff --git a/_module/ncs/69_hench_notevil.ncs b/_module/ncs/69_hench_notevil.ncs index 79e73ad1..fd613e5f 100644 Binary files a/_module/ncs/69_hench_notevil.ncs and b/_module/ncs/69_hench_notevil.ncs differ diff --git a/_module/ncs/69_hench_pickup.ncs b/_module/ncs/69_hench_pickup.ncs index a0e64d44..7238ed1a 100644 Binary files a/_module/ncs/69_hench_pickup.ncs and b/_module/ncs/69_hench_pickup.ncs differ diff --git a/_module/ncs/69_hench_quit.ncs b/_module/ncs/69_hench_quit.ncs index 72362f83..31c178b6 100644 Binary files a/_module/ncs/69_hench_quit.ncs and b/_module/ncs/69_hench_quit.ncs differ diff --git a/_module/ncs/69_hench_rangeno.ncs b/_module/ncs/69_hench_rangeno.ncs index abade2d1..cdb37e9e 100644 Binary files a/_module/ncs/69_hench_rangeno.ncs and b/_module/ncs/69_hench_rangeno.ncs differ diff --git a/_module/ncs/69_hench_rangeye.ncs b/_module/ncs/69_hench_rangeye.ncs index e6a1f6f7..627113c6 100644 Binary files a/_module/ncs/69_hench_rangeye.ncs and b/_module/ncs/69_hench_rangeye.ncs differ diff --git a/_module/ncs/69_hench_scout1.ncs b/_module/ncs/69_hench_scout1.ncs index ec27b38a..9713a052 100644 Binary files a/_module/ncs/69_hench_scout1.ncs and b/_module/ncs/69_hench_scout1.ncs differ diff --git a/_module/ncs/69_hench_scout2.ncs b/_module/ncs/69_hench_scout2.ncs index 07b75d86..06219708 100644 Binary files a/_module/ncs/69_hench_scout2.ncs and b/_module/ncs/69_hench_scout2.ncs differ diff --git a/_module/ncs/69_hench_switch.ncs b/_module/ncs/69_hench_switch.ncs index 0f076e1a..2c780ef5 100644 Binary files a/_module/ncs/69_hench_switch.ncs and b/_module/ncs/69_hench_switch.ncs differ diff --git a/_module/ncs/69_hench_switchn.ncs b/_module/ncs/69_hench_switchn.ncs index c7a5c8cf..85eb5fcc 100644 Binary files a/_module/ncs/69_hench_switchn.ncs and b/_module/ncs/69_hench_switchn.ncs differ diff --git a/_module/ncs/69_henchscoutno.ncs b/_module/ncs/69_henchscoutno.ncs index 9d7da327..96cec1fd 100644 Binary files a/_module/ncs/69_henchscoutno.ncs and b/_module/ncs/69_henchscoutno.ncs differ diff --git a/_module/ncs/_area_on_enter.ncs b/_module/ncs/_area_on_enter.ncs index 0bacb1cb..a1cf9353 100644 Binary files a/_module/ncs/_area_on_enter.ncs and b/_module/ncs/_area_on_enter.ncs differ diff --git a/_module/ncs/_area_on_enter_m.ncs b/_module/ncs/_area_on_enter_m.ncs index 6832d8a0..efeadd64 100644 Binary files a/_module/ncs/_area_on_enter_m.ncs and b/_module/ncs/_area_on_enter_m.ncs differ diff --git a/_module/ncs/_area_on_exit.ncs b/_module/ncs/_area_on_exit.ncs index d228dd57..baba0a53 100644 Binary files a/_module/ncs/_area_on_exit.ncs and b/_module/ncs/_area_on_exit.ncs differ diff --git a/_module/ncs/_area_open_map.ncs b/_module/ncs/_area_open_map.ncs index 7a2686ea..1d5356cf 100644 Binary files a/_module/ncs/_area_open_map.ncs and b/_module/ncs/_area_open_map.ncs differ diff --git a/_module/ncs/_boss_spawn_def9.ncs b/_module/ncs/_boss_spawn_def9.ncs index e50a0347..e25f1eb2 100644 Binary files a/_module/ncs/_boss_spawn_def9.ncs and b/_module/ncs/_boss_spawn_def9.ncs differ diff --git a/_module/ncs/_deadmagic_enter.ncs b/_module/ncs/_deadmagic_enter.ncs index 692aa500..c13a7f01 100644 Binary files a/_module/ncs/_deadmagic_enter.ncs and b/_module/ncs/_deadmagic_enter.ncs differ diff --git a/_module/ncs/_deadmagic_exit.ncs b/_module/ncs/_deadmagic_exit.ncs index eae4328b..506e8100 100644 Binary files a/_module/ncs/_deadmagic_exit.ncs and b/_module/ncs/_deadmagic_exit.ncs differ diff --git a/_module/ncs/_fb1_onenter.ncs b/_module/ncs/_fb1_onenter.ncs index 0c62b44a..c71ced39 100644 Binary files a/_module/ncs/_fb1_onenter.ncs and b/_module/ncs/_fb1_onenter.ncs differ diff --git a/_module/ncs/_golemeqgear.ncs b/_module/ncs/_golemeqgear.ncs index 94ddb7b9..e02554f7 100644 Binary files a/_module/ncs/_golemeqgear.ncs and b/_module/ncs/_golemeqgear.ncs differ diff --git a/_module/ncs/_golemstats.ncs b/_module/ncs/_golemstats.ncs index ae00bb2d..9e67ff2c 100644 Binary files a/_module/ncs/_golemstats.ncs and b/_module/ncs/_golemstats.ncs differ diff --git a/_module/ncs/_innerkeep_enter.ncs b/_module/ncs/_innerkeep_enter.ncs index c9c480ca..2a056381 100644 Binary files a/_module/ncs/_innerkeep_enter.ncs and b/_module/ncs/_innerkeep_enter.ncs differ diff --git a/_module/ncs/_log_in_fix.ncs b/_module/ncs/_log_in_fix.ncs index 7b174065..2f0ce824 100644 Binary files a/_module/ncs/_log_in_fix.ncs and b/_module/ncs/_log_in_fix.ncs differ diff --git a/_module/ncs/_log_out_fix.ncs b/_module/ncs/_log_out_fix.ncs index 27bddc7f..1ff93bdb 100644 Binary files a/_module/ncs/_log_out_fix.ncs and b/_module/ncs/_log_out_fix.ncs differ diff --git a/_module/ncs/_ness_spawn_hb.ncs b/_module/ncs/_ness_spawn_hb.ncs index 1ec60f87..58c1b943 100644 Binary files a/_module/ncs/_ness_spawn_hb.ncs and b/_module/ncs/_ness_spawn_hb.ncs differ diff --git a/_module/ncs/_on_cliententer.ncs b/_module/ncs/_on_cliententer.ncs index 54030797..7d91d3c1 100644 Binary files a/_module/ncs/_on_cliententer.ncs and b/_module/ncs/_on_cliententer.ncs differ diff --git a/_module/ncs/_on_respawn.ncs b/_module/ncs/_on_respawn.ncs index 560ff87b..ff59a876 100644 Binary files a/_module/ncs/_on_respawn.ncs and b/_module/ncs/_on_respawn.ncs differ diff --git a/_module/ncs/_on_unacquire.ncs b/_module/ncs/_on_unacquire.ncs index c0ddf0f0..91e8053f 100644 Binary files a/_module/ncs/_on_unacquire.ncs and b/_module/ncs/_on_unacquire.ncs differ diff --git a/_module/ncs/_onacquire.ncs b/_module/ncs/_onacquire.ncs index 1d29252d..98782395 100644 Binary files a/_module/ncs/_onacquire.ncs and b/_module/ncs/_onacquire.ncs differ diff --git a/_module/ncs/_oncliententer.ncs b/_module/ncs/_oncliententer.ncs index 016f21fa..edcbdd3a 100644 Binary files a/_module/ncs/_oncliententer.ncs and b/_module/ncs/_oncliententer.ncs differ diff --git a/_module/ncs/_ondeath.ncs b/_module/ncs/_ondeath.ncs index 56b2d345..10e55413 100644 Binary files a/_module/ncs/_ondeath.ncs and b/_module/ncs/_ondeath.ncs differ diff --git a/_module/ncs/_onplayer_rest.ncs b/_module/ncs/_onplayer_rest.ncs index 4b3f3160..1953e34f 100644 Binary files a/_module/ncs/_onplayer_rest.ncs and b/_module/ncs/_onplayer_rest.ncs differ diff --git a/_module/ncs/_onplayerlevelup.ncs b/_module/ncs/_onplayerlevelup.ncs index 0a71e18a..14665ebf 100644 Binary files a/_module/ncs/_onplayerlevelup.ncs and b/_module/ncs/_onplayerlevelup.ncs differ diff --git a/_module/ncs/_rg_ale.ncs b/_module/ncs/_rg_ale.ncs index 767825b6..c44e0264 100644 Binary files a/_module/ncs/_rg_ale.ncs and b/_module/ncs/_rg_ale.ncs differ diff --git a/_module/ncs/_rg_spirits.ncs b/_module/ncs/_rg_spirits.ncs index 235e3d4b..1a7bb4d4 100644 Binary files a/_module/ncs/_rg_spirits.ncs and b/_module/ncs/_rg_spirits.ncs differ diff --git a/_module/ncs/_rg_wine.ncs b/_module/ncs/_rg_wine.ncs index 388fc052..83e8351c 100644 Binary files a/_module/ncs/_rg_wine.ncs and b/_module/ncs/_rg_wine.ncs differ diff --git a/_module/ncs/_rg_wine_stain.ncs b/_module/ncs/_rg_wine_stain.ncs index 152c0fe4..04a0bfca 100644 Binary files a/_module/ncs/_rg_wine_stain.ncs and b/_module/ncs/_rg_wine_stain.ncs differ diff --git a/_module/ncs/_wildmagic_enter.ncs b/_module/ncs/_wildmagic_enter.ncs index aa30afdf..ea016494 100644 Binary files a/_module/ncs/_wildmagic_enter.ncs and b/_module/ncs/_wildmagic_enter.ncs differ diff --git a/_module/ncs/aa_rod_of_wonder.ncs b/_module/ncs/aa_rod_of_wonder.ncs index e698ee52..ea01ea4c 100644 Binary files a/_module/ncs/aa_rod_of_wonder.ncs and b/_module/ncs/aa_rod_of_wonder.ncs differ diff --git a/_module/ncs/aa_wild_magic.ncs b/_module/ncs/aa_wild_magic.ncs index 64e193b8..9277415f 100644 Binary files a/_module/ncs/aa_wild_magic.ncs and b/_module/ncs/aa_wild_magic.ncs differ diff --git a/_module/ncs/abyss_ondeath.ncs b/_module/ncs/abyss_ondeath.ncs index 67736a74..550f49b9 100644 Binary files a/_module/ncs/abyss_ondeath.ncs and b/_module/ncs/abyss_ondeath.ncs differ diff --git a/_module/ncs/ai_a_ambusher.ncs b/_module/ncs/ai_a_ambusher.ncs new file mode 100644 index 00000000..f8a39ff2 Binary files /dev/null and b/_module/ncs/ai_a_ambusher.ncs differ diff --git a/_module/ncs/ai_a_atk_casters.ncs b/_module/ncs/ai_a_atk_casters.ncs new file mode 100644 index 00000000..2a6f4a58 Binary files /dev/null and b/_module/ncs/ai_a_atk_casters.ncs differ diff --git a/_module/ncs/ai_a_atk_nearest.ncs b/_module/ncs/ai_a_atk_nearest.ncs new file mode 100644 index 00000000..3cd45e81 Binary files /dev/null and b/_module/ncs/ai_a_atk_nearest.ncs differ diff --git a/_module/ncs/ai_a_atk_warrior.ncs b/_module/ncs/ai_a_atk_warrior.ncs new file mode 100644 index 00000000..5f160ad6 Binary files /dev/null and b/_module/ncs/ai_a_atk_warrior.ncs differ diff --git a/_module/ncs/ai_a_barbarian.ncs b/_module/ncs/ai_a_barbarian.ncs new file mode 100644 index 00000000..fa8149d6 Binary files /dev/null and b/_module/ncs/ai_a_barbarian.ncs differ diff --git a/_module/ncs/ai_a_bard.ncs b/_module/ncs/ai_a_bard.ncs new file mode 100644 index 00000000..6873015e Binary files /dev/null and b/_module/ncs/ai_a_bard.ncs differ diff --git a/_module/ncs/ai_a_cleric.ncs b/_module/ncs/ai_a_cleric.ncs new file mode 100644 index 00000000..d0856ac3 Binary files /dev/null and b/_module/ncs/ai_a_cleric.ncs differ diff --git a/_module/ncs/ai_a_cntrspell.ncs b/_module/ncs/ai_a_cntrspell.ncs new file mode 100644 index 00000000..22cf4287 Binary files /dev/null and b/_module/ncs/ai_a_cntrspell.ncs differ diff --git a/_module/ncs/ai_a_default.ncs b/_module/ncs/ai_a_default.ncs new file mode 100644 index 00000000..e5648b9e Binary files /dev/null and b/_module/ncs/ai_a_default.ncs differ diff --git a/_module/ncs/ai_a_defensive.ncs b/_module/ncs/ai_a_defensive.ncs new file mode 100644 index 00000000..ebb7f248 Binary files /dev/null and b/_module/ncs/ai_a_defensive.ncs differ diff --git a/_module/ncs/ai_a_druid.ncs b/_module/ncs/ai_a_druid.ncs new file mode 100644 index 00000000..fe29ce13 Binary files /dev/null and b/_module/ncs/ai_a_druid.ncs differ diff --git a/_module/ncs/ai_a_fighter.ncs b/_module/ncs/ai_a_fighter.ncs new file mode 100644 index 00000000..17dfd52d Binary files /dev/null and b/_module/ncs/ai_a_fighter.ncs differ diff --git a/_module/ncs/ai_a_flanker.ncs b/_module/ncs/ai_a_flanker.ncs new file mode 100644 index 00000000..cc1a22bd Binary files /dev/null and b/_module/ncs/ai_a_flanker.ncs differ diff --git a/_module/ncs/ai_a_invisible.ncs b/_module/ncs/ai_a_invisible.ncs new file mode 100644 index 00000000..d1afd373 Binary files /dev/null and b/_module/ncs/ai_a_invisible.ncs differ diff --git a/_module/ncs/ai_a_monk.ncs b/_module/ncs/ai_a_monk.ncs new file mode 100644 index 00000000..3b7d9ae6 Binary files /dev/null and b/_module/ncs/ai_a_monk.ncs differ diff --git a/_module/ncs/ai_a_no_cmb_mode.ncs b/_module/ncs/ai_a_no_cmb_mode.ncs new file mode 100644 index 00000000..86acace2 Binary files /dev/null and b/_module/ncs/ai_a_no_cmb_mode.ncs differ diff --git a/_module/ncs/ai_a_paladin.ncs b/_module/ncs/ai_a_paladin.ncs new file mode 100644 index 00000000..bf5d17db Binary files /dev/null and b/_module/ncs/ai_a_paladin.ncs differ diff --git a/_module/ncs/ai_a_peaceful.ncs b/_module/ncs/ai_a_peaceful.ncs new file mode 100644 index 00000000..ddc1baa8 Binary files /dev/null and b/_module/ncs/ai_a_peaceful.ncs differ diff --git a/_module/ncs/ai_a_polymorphed.ncs b/_module/ncs/ai_a_polymorphed.ncs new file mode 100644 index 00000000..40b87a39 Binary files /dev/null and b/_module/ncs/ai_a_polymorphed.ncs differ diff --git a/_module/ncs/ai_a_ranged.ncs b/_module/ncs/ai_a_ranged.ncs new file mode 100644 index 00000000..40c194f7 Binary files /dev/null and b/_module/ncs/ai_a_ranged.ncs differ diff --git a/_module/ncs/ai_a_ranger.ncs b/_module/ncs/ai_a_ranger.ncs new file mode 100644 index 00000000..f426e0e4 Binary files /dev/null and b/_module/ncs/ai_a_ranger.ncs differ diff --git a/_module/ncs/ai_a_rogue.ncs b/_module/ncs/ai_a_rogue.ncs new file mode 100644 index 00000000..a2680a11 Binary files /dev/null and b/_module/ncs/ai_a_rogue.ncs differ diff --git a/_module/ncs/ai_a_sorcerer.ncs b/_module/ncs/ai_a_sorcerer.ncs new file mode 100644 index 00000000..d46e0529 Binary files /dev/null and b/_module/ncs/ai_a_sorcerer.ncs differ diff --git a/_module/ncs/ai_a_taunter.ncs b/_module/ncs/ai_a_taunter.ncs new file mode 100644 index 00000000..2ff167c1 Binary files /dev/null and b/_module/ncs/ai_a_taunter.ncs differ diff --git a/_module/ncs/ai_a_wizard.ncs b/_module/ncs/ai_a_wizard.ncs new file mode 100644 index 00000000..40ec2fd6 Binary files /dev/null and b/_module/ncs/ai_a_wizard.ncs differ diff --git a/_module/ncs/ai_ambusher.ncs b/_module/ncs/ai_ambusher.ncs new file mode 100644 index 00000000..847db874 Binary files /dev/null and b/_module/ncs/ai_ambusher.ncs differ diff --git a/_module/ncs/ai_barbarian.ncs b/_module/ncs/ai_barbarian.ncs new file mode 100644 index 00000000..8bb340a3 Binary files /dev/null and b/_module/ncs/ai_barbarian.ncs differ diff --git a/_module/ncs/ai_bard.ncs b/_module/ncs/ai_bard.ncs new file mode 100644 index 00000000..63e430da Binary files /dev/null and b/_module/ncs/ai_bard.ncs differ diff --git a/_module/ncs/ai_cleric.ncs b/_module/ncs/ai_cleric.ncs new file mode 100644 index 00000000..88737c4c Binary files /dev/null and b/_module/ncs/ai_cleric.ncs differ diff --git a/_module/ncs/ai_cntrspell.ncs b/_module/ncs/ai_cntrspell.ncs new file mode 100644 index 00000000..2a7b3220 Binary files /dev/null and b/_module/ncs/ai_cntrspell.ncs differ diff --git a/_module/ncs/ai_coward.ncs b/_module/ncs/ai_coward.ncs new file mode 100644 index 00000000..e213b10a Binary files /dev/null and b/_module/ncs/ai_coward.ncs differ diff --git a/_module/ncs/ai_default.ncs b/_module/ncs/ai_default.ncs new file mode 100644 index 00000000..67084069 Binary files /dev/null and b/_module/ncs/ai_default.ncs differ diff --git a/_module/ncs/ai_defensive.ncs b/_module/ncs/ai_defensive.ncs new file mode 100644 index 00000000..31635481 Binary files /dev/null and b/_module/ncs/ai_defensive.ncs differ diff --git a/_module/ncs/ai_dragon.ncs b/_module/ncs/ai_dragon.ncs new file mode 100644 index 00000000..02e08a62 Binary files /dev/null and b/_module/ncs/ai_dragon.ncs differ diff --git a/_module/ncs/ai_druid.ncs b/_module/ncs/ai_druid.ncs new file mode 100644 index 00000000..ca082adb Binary files /dev/null and b/_module/ncs/ai_druid.ncs differ diff --git a/_module/ncs/ai_fighter.ncs b/_module/ncs/ai_fighter.ncs new file mode 100644 index 00000000..d8fd83bc Binary files /dev/null and b/_module/ncs/ai_fighter.ncs differ diff --git a/_module/ncs/ai_flanker.ncs b/_module/ncs/ai_flanker.ncs new file mode 100644 index 00000000..f5aa767d Binary files /dev/null and b/_module/ncs/ai_flanker.ncs differ diff --git a/_module/ncs/ai_incorporeal.ncs b/_module/ncs/ai_incorporeal.ncs new file mode 100644 index 00000000..4bde59e6 Binary files /dev/null and b/_module/ncs/ai_incorporeal.ncs differ diff --git a/_module/ncs/ai_invisible.ncs b/_module/ncs/ai_invisible.ncs new file mode 100644 index 00000000..7d7504c2 Binary files /dev/null and b/_module/ncs/ai_invisible.ncs differ diff --git a/_module/ncs/ai_monk.ncs b/_module/ncs/ai_monk.ncs new file mode 100644 index 00000000..cef19a51 Binary files /dev/null and b/_module/ncs/ai_monk.ncs differ diff --git a/_module/ncs/ai_paladin.ncs b/_module/ncs/ai_paladin.ncs new file mode 100644 index 00000000..a60c6164 Binary files /dev/null and b/_module/ncs/ai_paladin.ncs differ diff --git a/_module/ncs/ai_polymorphed.ncs b/_module/ncs/ai_polymorphed.ncs new file mode 100644 index 00000000..3f22f782 Binary files /dev/null and b/_module/ncs/ai_polymorphed.ncs differ diff --git a/_module/ncs/ai_ranged.ncs b/_module/ncs/ai_ranged.ncs new file mode 100644 index 00000000..bebde984 Binary files /dev/null and b/_module/ncs/ai_ranged.ncs differ diff --git a/_module/ncs/ai_ranger.ncs b/_module/ncs/ai_ranger.ncs new file mode 100644 index 00000000..c491cfdd Binary files /dev/null and b/_module/ncs/ai_ranger.ncs differ diff --git a/_module/ncs/ai_rogue.ncs b/_module/ncs/ai_rogue.ncs new file mode 100644 index 00000000..341513b0 Binary files /dev/null and b/_module/ncs/ai_rogue.ncs differ diff --git a/_module/ncs/ai_shadow.ncs b/_module/ncs/ai_shadow.ncs new file mode 100644 index 00000000..383918f3 Binary files /dev/null and b/_module/ncs/ai_shadow.ncs differ diff --git a/_module/ncs/ai_sorcerer.ncs b/_module/ncs/ai_sorcerer.ncs new file mode 100644 index 00000000..563ead68 Binary files /dev/null and b/_module/ncs/ai_sorcerer.ncs differ diff --git a/_module/ncs/ai_taunter.ncs b/_module/ncs/ai_taunter.ncs new file mode 100644 index 00000000..447b59fa Binary files /dev/null and b/_module/ncs/ai_taunter.ncs differ diff --git a/_module/ncs/ai_wizard.ncs b/_module/ncs/ai_wizard.ncs new file mode 100644 index 00000000..730fe5e9 Binary files /dev/null and b/_module/ncs/ai_wizard.ncs differ diff --git a/_module/ncs/air_door_open.ncs b/_module/ncs/air_door_open.ncs index ccb90aeb..0474f90e 100644 Binary files a/_module/ncs/air_door_open.ncs and b/_module/ncs/air_door_open.ncs differ diff --git a/_module/ncs/align2good.ncs b/_module/ncs/align2good.ncs index 30642353..7028da98 100644 Binary files a/_module/ncs/align2good.ncs and b/_module/ncs/align2good.ncs differ diff --git a/_module/ncs/aligncheckevil.ncs b/_module/ncs/aligncheckevil.ncs index 3ee9f5e2..cc9ae0d5 100644 Binary files a/_module/ncs/aligncheckevil.ncs and b/_module/ncs/aligncheckevil.ncs differ diff --git a/_module/ncs/aligncheckgood.ncs b/_module/ncs/aligncheckgood.ncs index 1aee62bd..a447fcc2 100644 Binary files a/_module/ncs/aligncheckgood.ncs and b/_module/ncs/aligncheckgood.ncs differ diff --git a/_module/ncs/alignchecklgood.ncs b/_module/ncs/alignchecklgood.ncs index 18cf37d3..81244121 100644 Binary files a/_module/ncs/alignchecklgood.ncs and b/_module/ncs/alignchecklgood.ncs differ diff --git a/_module/ncs/aligncheckneut.ncs b/_module/ncs/aligncheckneut.ncs index 537526c1..2f91b01e 100644 Binary files a/_module/ncs/aligncheckneut.ncs and b/_module/ncs/aligncheckneut.ncs differ diff --git a/_module/ncs/aligncheckngood.ncs b/_module/ncs/aligncheckngood.ncs index c7197c03..558e1731 100644 Binary files a/_module/ncs/aligncheckngood.ncs and b/_module/ncs/aligncheckngood.ncs differ diff --git a/_module/ncs/all_statues.ncs b/_module/ncs/all_statues.ncs index ab2fc0b6..da6150d2 100644 Binary files a/_module/ncs/all_statues.ncs and b/_module/ncs/all_statues.ncs differ diff --git a/_module/ncs/alreadyspoken.ncs b/_module/ncs/alreadyspoken.ncs index 118dd63a..aa26d127 100644 Binary files a/_module/ncs/alreadyspoken.ncs and b/_module/ncs/alreadyspoken.ncs differ diff --git a/_module/ncs/alter_glow_1.ncs b/_module/ncs/alter_glow_1.ncs index f5ece839..55a5ac30 100644 Binary files a/_module/ncs/alter_glow_1.ncs and b/_module/ncs/alter_glow_1.ncs differ diff --git a/_module/ncs/angrydruid1.ncs b/_module/ncs/angrydruid1.ncs index 0da821e0..f17c5a51 100644 Binary files a/_module/ncs/angrydruid1.ncs and b/_module/ncs/angrydruid1.ncs differ diff --git a/_module/ncs/anna_give.ncs b/_module/ncs/anna_give.ncs index 924aebc2..bf39b3b3 100644 Binary files a/_module/ncs/anna_give.ncs and b/_module/ncs/anna_give.ncs differ diff --git a/_module/ncs/anna_stat_shame.ncs b/_module/ncs/anna_stat_shame.ncs index bb217335..becdb5c2 100644 Binary files a/_module/ncs/anna_stat_shame.ncs and b/_module/ncs/anna_stat_shame.ncs differ diff --git a/_module/ncs/archery_ondamage.ncs b/_module/ncs/archery_ondamage.ncs index cb160175..ae36548a 100644 Binary files a/_module/ncs/archery_ondamage.ncs and b/_module/ncs/archery_ondamage.ncs differ diff --git a/_module/ncs/archon_ondeath.ncs b/_module/ncs/archon_ondeath.ncs index 06e6afc4..63563897 100644 Binary files a/_module/ncs/archon_ondeath.ncs and b/_module/ncs/archon_ondeath.ncs differ diff --git a/_module/ncs/area_cold_frost2.ncs b/_module/ncs/area_cold_frost2.ncs index 2bebd180..c13cc1bd 100644 Binary files a/_module/ncs/area_cold_frost2.ncs and b/_module/ncs/area_cold_frost2.ncs differ diff --git a/_module/ncs/area_spawner.ncs b/_module/ncs/area_spawner.ncs index d02dbcf7..c0b8f0c1 100644 Binary files a/_module/ncs/area_spawner.ncs and b/_module/ncs/area_spawner.ncs differ diff --git a/_module/ncs/areacleanup.ncs b/_module/ncs/areacleanup.ncs index 8969c22c..26d610da 100644 Binary files a/_module/ncs/areacleanup.ncs and b/_module/ncs/areacleanup.ncs differ diff --git a/_module/ncs/arrow_hail.ncs b/_module/ncs/arrow_hail.ncs index 9f2fd881..0eb11045 100644 Binary files a/_module/ncs/arrow_hail.ncs and b/_module/ncs/arrow_hail.ncs differ diff --git a/_module/ncs/asg_alcdesk_01t.ncs b/_module/ncs/asg_alcdesk_01t.ncs index dd53d424..aae28c6c 100644 Binary files a/_module/ncs/asg_alcdesk_01t.ncs and b/_module/ncs/asg_alcdesk_01t.ncs differ diff --git a/_module/ncs/asg_alcdsk_mat01.ncs b/_module/ncs/asg_alcdsk_mat01.ncs index 0891c1d6..d439f625 100644 Binary files a/_module/ncs/asg_alcdsk_mat01.ncs and b/_module/ncs/asg_alcdsk_mat01.ncs differ diff --git a/_module/ncs/asg_enchan_01t.ncs b/_module/ncs/asg_enchan_01t.ncs index 0a35c466..3f583f5e 100644 Binary files a/_module/ncs/asg_enchan_01t.ncs and b/_module/ncs/asg_enchan_01t.ncs differ diff --git a/_module/ncs/asg_racanebk.ncs b/_module/ncs/asg_racanebk.ncs index 6dde003d..03fd2b10 100644 Binary files a/_module/ncs/asg_racanebk.ncs and b/_module/ncs/asg_racanebk.ncs differ diff --git a/_module/ncs/asg_radvopen.ncs b/_module/ncs/asg_radvopen.ncs index 42e0c1e5..75de6d1b 100644 Binary files a/_module/ncs/asg_radvopen.ncs and b/_module/ncs/asg_radvopen.ncs differ diff --git a/_module/ncs/asg_rarcopen.ncs b/_module/ncs/asg_rarcopen.ncs index 74aa38f4..b212eb1c 100644 Binary files a/_module/ncs/asg_rarcopen.ncs and b/_module/ncs/asg_rarcopen.ncs differ diff --git a/_module/ncs/asg_rdivinebk.ncs b/_module/ncs/asg_rdivinebk.ncs index 6e1549e6..2e01319e 100644 Binary files a/_module/ncs/asg_rdivinebk.ncs and b/_module/ncs/asg_rdivinebk.ncs differ diff --git a/_module/ncs/asg_rdivopen.ncs b/_module/ncs/asg_rdivopen.ncs index b7c6e1b7..cf679ab0 100644 Binary files a/_module/ncs/asg_rdivopen.ncs and b/_module/ncs/asg_rdivopen.ncs differ diff --git a/_module/ncs/asg_resdesk_01.ncs b/_module/ncs/asg_resdesk_01.ncs index a13dcd85..11a85521 100644 Binary files a/_module/ncs/asg_resdesk_01.ncs and b/_module/ncs/asg_resdesk_01.ncs differ diff --git a/_module/ncs/asg_resdesk_01t.ncs b/_module/ncs/asg_resdesk_01t.ncs index bbe4c600..a1c3153a 100644 Binary files a/_module/ncs/asg_resdesk_01t.ncs and b/_module/ncs/asg_resdesk_01t.ncs differ diff --git a/_module/ncs/asg_resdesk_02.ncs b/_module/ncs/asg_resdesk_02.ncs index 36819b48..384d6bc6 100644 Binary files a/_module/ncs/asg_resdesk_02.ncs and b/_module/ncs/asg_resdesk_02.ncs differ diff --git a/_module/ncs/asg_resdesk_03.ncs b/_module/ncs/asg_resdesk_03.ncs index 6bd6c4fb..4af89735 100644 Binary files a/_module/ncs/asg_resdesk_03.ncs and b/_module/ncs/asg_resdesk_03.ncs differ diff --git a/_module/ncs/asg_resdesk_04t.ncs b/_module/ncs/asg_resdesk_04t.ncs index be4ff362..5b8af738 100644 Binary files a/_module/ncs/asg_resdesk_04t.ncs and b/_module/ncs/asg_resdesk_04t.ncs differ diff --git a/_module/ncs/asg_resdesk_05t.ncs b/_module/ncs/asg_resdesk_05t.ncs index f79e173d..6d8e20b0 100644 Binary files a/_module/ncs/asg_resdesk_05t.ncs and b/_module/ncs/asg_resdesk_05t.ncs differ diff --git a/_module/ncs/asg_resdesk_06t.ncs b/_module/ncs/asg_resdesk_06t.ncs index 7cf23c98..95ba9cde 100644 Binary files a/_module/ncs/asg_resdesk_06t.ncs and b/_module/ncs/asg_resdesk_06t.ncs differ diff --git a/_module/ncs/asg_resdesk_07.ncs b/_module/ncs/asg_resdesk_07.ncs index 9fdfb809..2b662312 100644 Binary files a/_module/ncs/asg_resdesk_07.ncs and b/_module/ncs/asg_resdesk_07.ncs differ diff --git a/_module/ncs/asg_resdesknext.ncs b/_module/ncs/asg_resdesknext.ncs index 003b0a98..69061b5f 100644 Binary files a/_module/ncs/asg_resdesknext.ncs and b/_module/ncs/asg_resdesknext.ncs differ diff --git a/_module/ncs/asg_resdeskprev.ncs b/_module/ncs/asg_resdeskprev.ncs index 981a1d76..d7140f98 100644 Binary files a/_module/ncs/asg_resdeskprev.ncs and b/_module/ncs/asg_resdeskprev.ncs differ diff --git a/_module/ncs/asg_rseedbooks.ncs b/_module/ncs/asg_rseedbooks.ncs index 1306215c..ae916778 100644 Binary files a/_module/ncs/asg_rseedbooks.ncs and b/_module/ncs/asg_rseedbooks.ncs differ diff --git a/_module/ncs/asg_rul_bulditem.ncs b/_module/ncs/asg_rul_bulditem.ncs index d43b316a..c6d0fda3 100644 Binary files a/_module/ncs/asg_rul_bulditem.ncs and b/_module/ncs/asg_rul_bulditem.ncs differ diff --git a/_module/ncs/asg_rul_buldmagi.ncs b/_module/ncs/asg_rul_buldmagi.ncs index ca6cf513..648932d3 100644 Binary files a/_module/ncs/asg_rul_buldmagi.ncs and b/_module/ncs/asg_rul_buldmagi.ncs differ diff --git a/_module/ncs/asg_rul_buldresr.ncs b/_module/ncs/asg_rul_buldresr.ncs index 00ec0496..0c13bcc4 100644 Binary files a/_module/ncs/asg_rul_buldresr.ncs and b/_module/ncs/asg_rul_buldresr.ncs differ diff --git a/_module/ncs/asg_rul_buldsmcl.ncs b/_module/ncs/asg_rul_buldsmcl.ncs index 429a5c70..43879ba4 100644 Binary files a/_module/ncs/asg_rul_buldsmcl.ncs and b/_module/ncs/asg_rul_buldsmcl.ncs differ diff --git a/_module/ncs/asg_rul_destcont.ncs b/_module/ncs/asg_rul_destcont.ncs index 72bfb5be..cf3b157d 100644 Binary files a/_module/ncs/asg_rul_destcont.ncs and b/_module/ncs/asg_rul_destcont.ncs differ diff --git a/_module/ncs/asg_rul_micscfx.ncs b/_module/ncs/asg_rul_micscfx.ncs index 1f9c8745..9876bd7e 100644 Binary files a/_module/ncs/asg_rul_micscfx.ncs and b/_module/ncs/asg_rul_micscfx.ncs differ diff --git a/_module/ncs/asg_rul_micstore.ncs b/_module/ncs/asg_rul_micstore.ncs index 0184c32f..93d6b348 100644 Binary files a/_module/ncs/asg_rul_micstore.ncs and b/_module/ncs/asg_rul_micstore.ncs differ diff --git a/_module/ncs/asg_rul_opestore.ncs b/_module/ncs/asg_rul_opestore.ncs index 6be78e11..bde5c965 100644 Binary files a/_module/ncs/asg_rul_opestore.ncs and b/_module/ncs/asg_rul_opestore.ncs differ diff --git a/_module/ncs/asg_rul_rescast.ncs b/_module/ncs/asg_rul_rescast.ncs index 71b94c03..af0b78c9 100644 Binary files a/_module/ncs/asg_rul_rescast.ncs and b/_module/ncs/asg_rul_rescast.ncs differ diff --git a/_module/ncs/asg_rul_testbmag.ncs b/_module/ncs/asg_rul_testbmag.ncs index 1211dae0..f1b2db09 100644 Binary files a/_module/ncs/asg_rul_testbmag.ncs and b/_module/ncs/asg_rul_testbmag.ncs differ diff --git a/_module/ncs/asg_rul_testbsel.ncs b/_module/ncs/asg_rul_testbsel.ncs index f4a40c26..ac023f35 100644 Binary files a/_module/ncs/asg_rul_testbsel.ncs and b/_module/ncs/asg_rul_testbsel.ncs differ diff --git a/_module/ncs/asg_rul_testmcop.ncs b/_module/ncs/asg_rul_testmcop.ncs index 32dca0b5..627f80e8 100644 Binary files a/_module/ncs/asg_rul_testmcop.ncs and b/_module/ncs/asg_rul_testmcop.ncs differ diff --git a/_module/ncs/asg_rul_testop00.ncs b/_module/ncs/asg_rul_testop00.ncs index 29fd048d..2b3e1d4a 100644 Binary files a/_module/ncs/asg_rul_testop00.ncs and b/_module/ncs/asg_rul_testop00.ncs differ diff --git a/_module/ncs/asg_rul_testop01.ncs b/_module/ncs/asg_rul_testop01.ncs index 7b18fa0e..88775a58 100644 Binary files a/_module/ncs/asg_rul_testop01.ncs and b/_module/ncs/asg_rul_testop01.ncs differ diff --git a/_module/ncs/asg_rul_testop02.ncs b/_module/ncs/asg_rul_testop02.ncs index 7f4623a7..c1c7d891 100644 Binary files a/_module/ncs/asg_rul_testop02.ncs and b/_module/ncs/asg_rul_testop02.ncs differ diff --git a/_module/ncs/asg_rul_testop03.ncs b/_module/ncs/asg_rul_testop03.ncs index b1d32938..24842925 100644 Binary files a/_module/ncs/asg_rul_testop03.ncs and b/_module/ncs/asg_rul_testop03.ncs differ diff --git a/_module/ncs/asg_rul_testop04.ncs b/_module/ncs/asg_rul_testop04.ncs index 22e5c64e..767df096 100644 Binary files a/_module/ncs/asg_rul_testop04.ncs and b/_module/ncs/asg_rul_testop04.ncs differ diff --git a/_module/ncs/asg_rul_testop05.ncs b/_module/ncs/asg_rul_testop05.ncs index c1e0f09f..025e3b2a 100644 Binary files a/_module/ncs/asg_rul_testop05.ncs and b/_module/ncs/asg_rul_testop05.ncs differ diff --git a/_module/ncs/asg_rul_testop06.ncs b/_module/ncs/asg_rul_testop06.ncs index ce6aed6d..87574965 100644 Binary files a/_module/ncs/asg_rul_testop06.ncs and b/_module/ncs/asg_rul_testop06.ncs differ diff --git a/_module/ncs/asg_rul_testopre.ncs b/_module/ncs/asg_rul_testopre.ncs index 1c7d493d..478b75b4 100644 Binary files a/_module/ncs/asg_rul_testopre.ncs and b/_module/ncs/asg_rul_testopre.ncs differ diff --git a/_module/ncs/asg_rul_testsmcl.ncs b/_module/ncs/asg_rul_testsmcl.ncs index 08b09aeb..6f801cca 100644 Binary files a/_module/ncs/asg_rul_testsmcl.ncs and b/_module/ncs/asg_rul_testsmcl.ncs differ diff --git a/_module/ncs/asg_set_reshop01.ncs b/_module/ncs/asg_set_reshop01.ncs index 5036685d..2669017a 100644 Binary files a/_module/ncs/asg_set_reshop01.ncs and b/_module/ncs/asg_set_reshop01.ncs differ diff --git a/_module/ncs/asg_set_reshop02.ncs b/_module/ncs/asg_set_reshop02.ncs index 680d03ae..cd90334a 100644 Binary files a/_module/ncs/asg_set_reshop02.ncs and b/_module/ncs/asg_set_reshop02.ncs differ diff --git a/_module/ncs/asg_set_reshop03.ncs b/_module/ncs/asg_set_reshop03.ncs index 8f347182..a7446428 100644 Binary files a/_module/ncs/asg_set_reshop03.ncs and b/_module/ncs/asg_set_reshop03.ncs differ diff --git a/_module/ncs/asg_set_reshop04.ncs b/_module/ncs/asg_set_reshop04.ncs index dc6396c2..51ab5251 100644 Binary files a/_module/ncs/asg_set_reshop04.ncs and b/_module/ncs/asg_set_reshop04.ncs differ diff --git a/_module/ncs/asg_set_reshop05.ncs b/_module/ncs/asg_set_reshop05.ncs index 34f8d16d..669ac64a 100644 Binary files a/_module/ncs/asg_set_reshop05.ncs and b/_module/ncs/asg_set_reshop05.ncs differ diff --git a/_module/ncs/asg_set_reshop06.ncs b/_module/ncs/asg_set_reshop06.ncs index 2f6e6dd1..154b3a61 100644 Binary files a/_module/ncs/asg_set_reshop06.ncs and b/_module/ncs/asg_set_reshop06.ncs differ diff --git a/_module/ncs/asg_temp_rbooks.ncs b/_module/ncs/asg_temp_rbooks.ncs index 3e7b0e63..e179dda2 100644 Binary files a/_module/ncs/asg_temp_rbooks.ncs and b/_module/ncs/asg_temp_rbooks.ncs differ diff --git a/_module/ncs/asg_temp_rbooksd.ncs b/_module/ncs/asg_temp_rbooksd.ncs index 879baa92..07585a23 100644 Binary files a/_module/ncs/asg_temp_rbooksd.ncs and b/_module/ncs/asg_temp_rbooksd.ncs differ diff --git a/_module/ncs/asg_tes_alanvtim.ncs b/_module/ncs/asg_tes_alanvtim.ncs index 0b749a75..af3a8a0f 100644 Binary files a/_module/ncs/asg_tes_alanvtim.ncs and b/_module/ncs/asg_tes_alanvtim.ncs differ diff --git a/_module/ncs/asg_tes_alteranv.ncs b/_module/ncs/asg_tes_alteranv.ncs index c4848f18..7c1d7816 100644 Binary files a/_module/ncs/asg_tes_alteranv.ncs and b/_module/ncs/asg_tes_alteranv.ncs differ diff --git a/_module/ncs/asg_tes_anvaltrc.ncs b/_module/ncs/asg_tes_anvaltrc.ncs index ffedad4b..d65c23db 100644 Binary files a/_module/ncs/asg_tes_anvaltrc.ncs and b/_module/ncs/asg_tes_anvaltrc.ncs differ diff --git a/_module/ncs/asg_tes_mics1str.ncs b/_module/ncs/asg_tes_mics1str.ncs index 03a9311e..c439e416 100644 Binary files a/_module/ncs/asg_tes_mics1str.ncs and b/_module/ncs/asg_tes_mics1str.ncs differ diff --git a/_module/ncs/asg_tes_portlab.ncs b/_module/ncs/asg_tes_portlab.ncs index a2961187..f9a787b9 100644 Binary files a/_module/ncs/asg_tes_portlab.ncs and b/_module/ncs/asg_tes_portlab.ncs differ diff --git a/_module/ncs/asg_test_bdsrwiz.ncs b/_module/ncs/asg_test_bdsrwiz.ncs index 38a61e6d..909dee1d 100644 Binary files a/_module/ncs/asg_test_bdsrwiz.ncs and b/_module/ncs/asg_test_bdsrwiz.ncs differ diff --git a/_module/ncs/asg_test_cldrpal.ncs b/_module/ncs/asg_test_cldrpal.ncs index 2a52e551..60b3b37d 100644 Binary files a/_module/ncs/asg_test_cldrpal.ncs and b/_module/ncs/asg_test_cldrpal.ncs differ diff --git a/_module/ncs/at_dt_dragon_001.ncs b/_module/ncs/at_dt_dragon_001.ncs index 65723496..991739a3 100644 Binary files a/_module/ncs/at_dt_dragon_001.ncs and b/_module/ncs/at_dt_dragon_001.ncs differ diff --git a/_module/ncs/attack_pc.ncs b/_module/ncs/attack_pc.ncs index c7df375f..d6c2306a 100644 Binary files a/_module/ncs/attack_pc.ncs and b/_module/ncs/attack_pc.ncs differ diff --git a/_module/ncs/badger_death.ncs b/_module/ncs/badger_death.ncs index 915e4682..d41a5087 100644 Binary files a/_module/ncs/badger_death.ncs and b/_module/ncs/badger_death.ncs differ diff --git a/_module/ncs/baltar_ck_cmpgn.ncs b/_module/ncs/baltar_ck_cmpgn.ncs index e219d8f2..f3d3aa39 100644 Binary files a/_module/ncs/baltar_ck_cmpgn.ncs and b/_module/ncs/baltar_ck_cmpgn.ncs differ diff --git a/_module/ncs/baltar_done.ncs b/_module/ncs/baltar_done.ncs index f8ca4ed2..9255519f 100644 Binary files a/_module/ncs/baltar_done.ncs and b/_module/ncs/baltar_done.ncs differ diff --git a/_module/ncs/baltar_setvar.ncs b/_module/ncs/baltar_setvar.ncs index 2ba4794f..8580bd85 100644 Binary files a/_module/ncs/baltar_setvar.ncs and b/_module/ncs/baltar_setvar.ncs differ diff --git a/_module/ncs/baltar_spoken2.ncs b/_module/ncs/baltar_spoken2.ncs index 57ae5f90..fb731c3f 100644 Binary files a/_module/ncs/baltar_spoken2.ncs and b/_module/ncs/baltar_spoken2.ncs differ diff --git a/_module/ncs/baltarattack.ncs b/_module/ncs/baltarattack.ncs index c7df375f..d6c2306a 100644 Binary files a/_module/ncs/baltarattack.ncs and b/_module/ncs/baltarattack.ncs differ diff --git a/_module/ncs/bank_on_close.ncs b/_module/ncs/bank_on_close.ncs index 071c406a..67d97093 100644 Binary files a/_module/ncs/bank_on_close.ncs and b/_module/ncs/bank_on_close.ncs differ diff --git a/_module/ncs/bank_on_dist.ncs b/_module/ncs/bank_on_dist.ncs index 3109577f..c8e8e439 100644 Binary files a/_module/ncs/bank_on_dist.ncs and b/_module/ncs/bank_on_dist.ncs differ diff --git a/_module/ncs/bank_on_open.ncs b/_module/ncs/bank_on_open.ncs index e7ed3152..5dff67f7 100644 Binary files a/_module/ncs/bank_on_open.ncs and b/_module/ncs/bank_on_open.ncs differ diff --git a/_module/ncs/barb_ck.ncs b/_module/ncs/barb_ck.ncs index 0f0236e9..975ec731 100644 Binary files a/_module/ncs/barb_ck.ncs and b/_module/ncs/barb_ck.ncs differ diff --git a/_module/ncs/barkskinvfx.ncs b/_module/ncs/barkskinvfx.ncs index 3d1d00c1..5bc38ade 100644 Binary files a/_module/ncs/barkskinvfx.ncs and b/_module/ncs/barkskinvfx.ncs differ diff --git a/_module/ncs/barkskinvfx2.ncs b/_module/ncs/barkskinvfx2.ncs index 2dea2583..a8b9007b 100644 Binary files a/_module/ncs/barkskinvfx2.ncs and b/_module/ncs/barkskinvfx2.ncs differ diff --git a/_module/ncs/barrel_ondeath.ncs b/_module/ncs/barrel_ondeath.ncs index c63501be..9e013280 100644 Binary files a/_module/ncs/barrel_ondeath.ncs and b/_module/ncs/barrel_ondeath.ncs differ diff --git a/_module/ncs/beggar.ncs b/_module/ncs/beggar.ncs index 4edd8676..836b9518 100644 Binary files a/_module/ncs/beggar.ncs and b/_module/ncs/beggar.ncs differ diff --git a/_module/ncs/beggar_align2.ncs b/_module/ncs/beggar_align2.ncs index aba604cd..f68f9e73 100644 Binary files a/_module/ncs/beggar_align2.ncs and b/_module/ncs/beggar_align2.ncs differ diff --git a/_module/ncs/beholder_gland.ncs b/_module/ncs/beholder_gland.ncs index 2e21918d..cf243823 100644 Binary files a/_module/ncs/beholder_gland.ncs and b/_module/ncs/beholder_gland.ncs differ diff --git a/_module/ncs/blueteamuniform.ncs b/_module/ncs/blueteamuniform.ncs index 8da6b2a0..4b59a32c 100644 Binary files a/_module/ncs/blueteamuniform.ncs and b/_module/ncs/blueteamuniform.ncs differ diff --git a/_module/ncs/bomb.ncs b/_module/ncs/bomb.ncs index e99a793d..44822aac 100644 Binary files a/_module/ncs/bomb.ncs and b/_module/ncs/bomb.ncs differ diff --git a/_module/ncs/bomb_bang.ncs b/_module/ncs/bomb_bang.ncs index 2b3f0b88..b713956d 100644 Binary files a/_module/ncs/bomb_bang.ncs and b/_module/ncs/bomb_bang.ncs differ diff --git a/_module/ncs/bomb_onused.ncs b/_module/ncs/bomb_onused.ncs index 2f11b311..27d930cd 100644 Binary files a/_module/ncs/bomb_onused.ncs and b/_module/ncs/bomb_onused.ncs differ diff --git a/_module/ncs/bookcasepuzzle.ncs b/_module/ncs/bookcasepuzzle.ncs index 44624ce4..be6400b8 100644 Binary files a/_module/ncs/bookcasepuzzle.ncs and b/_module/ncs/bookcasepuzzle.ncs differ diff --git a/_module/ncs/breth_drow_port.ncs b/_module/ncs/breth_drow_port.ncs index 4ffe83f4..b3a652d7 100644 Binary files a/_module/ncs/breth_drow_port.ncs and b/_module/ncs/breth_drow_port.ncs differ diff --git a/_module/ncs/breth_nasport.ncs b/_module/ncs/breth_nasport.ncs index d109c1ab..a826ba8e 100644 Binary files a/_module/ncs/breth_nasport.ncs and b/_module/ncs/breth_nasport.ncs differ diff --git a/_module/ncs/breth_south_port.ncs b/_module/ncs/breth_south_port.ncs index 5f9af16a..d0ed9ddd 100644 Binary files a/_module/ncs/breth_south_port.ncs and b/_module/ncs/breth_south_port.ncs differ diff --git a/_module/ncs/catapult_die.ncs b/_module/ncs/catapult_die.ncs index db37ce9b..534c4056 100644 Binary files a/_module/ncs/catapult_die.ncs and b/_module/ncs/catapult_die.ncs differ diff --git a/_module/ncs/celest_ondeath.ncs b/_module/ncs/celest_ondeath.ncs index 76d269e8..3cfb0d91 100644 Binary files a/_module/ncs/celest_ondeath.ncs and b/_module/ncs/celest_ondeath.ncs differ diff --git a/_module/ncs/change_align_c.ncs b/_module/ncs/change_align_c.ncs index 3712326d..38a60aa1 100644 Binary files a/_module/ncs/change_align_c.ncs and b/_module/ncs/change_align_c.ncs differ diff --git a/_module/ncs/check_baltar.ncs b/_module/ncs/check_baltar.ncs index a0d487e4..bd6e1e04 100644 Binary files a/_module/ncs/check_baltar.ncs and b/_module/ncs/check_baltar.ncs differ diff --git a/_module/ncs/check_bandi_head.ncs b/_module/ncs/check_bandi_head.ncs index 05c58738..9e99ff5a 100644 Binary files a/_module/ncs/check_bandi_head.ncs and b/_module/ncs/check_bandi_head.ncs differ diff --git a/_module/ncs/check_cugelhead.ncs b/_module/ncs/check_cugelhead.ncs index 435f790d..ad766cf8 100644 Binary files a/_module/ncs/check_cugelhead.ncs and b/_module/ncs/check_cugelhead.ncs differ diff --git a/_module/ncs/check_cugelnote.ncs b/_module/ncs/check_cugelnote.ncs index e7faf614..e8428306 100644 Binary files a/_module/ncs/check_cugelnote.ncs and b/_module/ncs/check_cugelnote.ncs differ diff --git a/_module/ncs/check_diary.ncs b/_module/ncs/check_diary.ncs index ebc7948f..18741e75 100644 Binary files a/_module/ncs/check_diary.ncs and b/_module/ncs/check_diary.ncs differ diff --git a/_module/ncs/check_dragonbloo.ncs b/_module/ncs/check_dragonbloo.ncs index 73c40bab..d9b5d69a 100644 Binary files a/_module/ncs/check_dragonbloo.ncs and b/_module/ncs/check_dragonbloo.ncs differ diff --git a/_module/ncs/check_ele_ring.ncs b/_module/ncs/check_ele_ring.ncs index ffae1e22..0fc956ee 100644 Binary files a/_module/ncs/check_ele_ring.ncs and b/_module/ncs/check_ele_ring.ncs differ diff --git a/_module/ncs/check_giant_head.ncs b/_module/ncs/check_giant_head.ncs index 7eef92fc..4e080033 100644 Binary files a/_module/ncs/check_giant_head.ncs and b/_module/ncs/check_giant_head.ncs differ diff --git a/_module/ncs/check_gob_head.ncs b/_module/ncs/check_gob_head.ncs index 0d01a9c0..8038a113 100644 Binary files a/_module/ncs/check_gob_head.ncs and b/_module/ncs/check_gob_head.ncs differ diff --git a/_module/ncs/check_green.ncs b/_module/ncs/check_green.ncs index 4e2749ff..9c8a51c4 100644 Binary files a/_module/ncs/check_green.ncs and b/_module/ncs/check_green.ncs differ diff --git a/_module/ncs/check_hamleynote.ncs b/_module/ncs/check_hamleynote.ncs index 5773d7e3..c758e4b6 100644 Binary files a/_module/ncs/check_hamleynote.ncs and b/_module/ncs/check_hamleynote.ncs differ diff --git a/_module/ncs/check_inn_note.ncs b/_module/ncs/check_inn_note.ncs index a7aba2bb..7093326c 100644 Binary files a/_module/ncs/check_inn_note.ncs and b/_module/ncs/check_inn_note.ncs differ diff --git a/_module/ncs/check_kells_head.ncs b/_module/ncs/check_kells_head.ncs index bcdaa275..5a1eb3cd 100644 Binary files a/_module/ncs/check_kells_head.ncs and b/_module/ncs/check_kells_head.ncs differ diff --git a/_module/ncs/check_millbaby.ncs b/_module/ncs/check_millbaby.ncs index 909617a7..912a7683 100644 Binary files a/_module/ncs/check_millbaby.ncs and b/_module/ncs/check_millbaby.ncs differ diff --git a/_module/ncs/check_nurglehead.ncs b/_module/ncs/check_nurglehead.ncs index ab7df791..1f2f6b0a 100644 Binary files a/_module/ncs/check_nurglehead.ncs and b/_module/ncs/check_nurglehead.ncs differ diff --git a/_module/ncs/check_orb.ncs b/_module/ncs/check_orb.ncs index e5a8df75..a9e250c4 100644 Binary files a/_module/ncs/check_orb.ncs and b/_module/ncs/check_orb.ncs differ diff --git a/_module/ncs/check_orkus_ring.ncs b/_module/ncs/check_orkus_ring.ncs index 04448919..77302b54 100644 Binary files a/_module/ncs/check_orkus_ring.ncs and b/_module/ncs/check_orkus_ring.ncs differ diff --git a/_module/ncs/check_samurai.ncs b/_module/ncs/check_samurai.ncs index 969b93be..c304a548 100644 Binary files a/_module/ncs/check_samurai.ncs and b/_module/ncs/check_samurai.ncs differ diff --git a/_module/ncs/check_w_head.ncs b/_module/ncs/check_w_head.ncs index 0ee95f9c..be5f2e65 100644 Binary files a/_module/ncs/check_w_head.ncs and b/_module/ncs/check_w_head.ncs differ diff --git a/_module/ncs/chk_ang_dragon_1.ncs b/_module/ncs/chk_ang_dragon_1.ncs index c3f1c1ad..478ec44d 100644 Binary files a/_module/ncs/chk_ang_dragon_1.ncs and b/_module/ncs/chk_ang_dragon_1.ncs differ diff --git a/_module/ncs/chk_ang_dragon_2.ncs b/_module/ncs/chk_ang_dragon_2.ncs index 93f8cbb3..09399749 100644 Binary files a/_module/ncs/chk_ang_dragon_2.ncs and b/_module/ncs/chk_ang_dragon_2.ncs differ diff --git a/_module/ncs/chk_drag_persuad.ncs b/_module/ncs/chk_drag_persuad.ncs index f379040d..3826c6d2 100644 Binary files a/_module/ncs/chk_drag_persuad.ncs and b/_module/ncs/chk_drag_persuad.ncs differ diff --git a/_module/ncs/chk_dryad_1.ncs b/_module/ncs/chk_dryad_1.ncs index 5f52e7e7..a3e37441 100644 Binary files a/_module/ncs/chk_dryad_1.ncs and b/_module/ncs/chk_dryad_1.ncs differ diff --git a/_module/ncs/chk_dryad_2.ncs b/_module/ncs/chk_dryad_2.ncs index e685dcde..72f249f5 100644 Binary files a/_module/ncs/chk_dryad_2.ncs and b/_module/ncs/chk_dryad_2.ncs differ diff --git a/_module/ncs/chk_item_ent_gso.ncs b/_module/ncs/chk_item_ent_gso.ncs index ea8e7fe6..362cab21 100644 Binary files a/_module/ncs/chk_item_ent_gso.ncs and b/_module/ncs/chk_item_ent_gso.ncs differ diff --git a/_module/ncs/chk_item_ent_tmf.ncs b/_module/ncs/chk_item_ent_tmf.ncs index 7df785f8..4f8cd9de 100644 Binary files a/_module/ncs/chk_item_ent_tmf.ncs and b/_module/ncs/chk_item_ent_tmf.ncs differ diff --git a/_module/ncs/chk_merim_charis.ncs b/_module/ncs/chk_merim_charis.ncs index cc554ccd..4a0b0a7c 100644 Binary files a/_module/ncs/chk_merim_charis.ncs and b/_module/ncs/chk_merim_charis.ncs differ diff --git a/_module/ncs/chk_merimes_comp.ncs b/_module/ncs/chk_merimes_comp.ncs index 0cc6ec4c..941792ae 100644 Binary files a/_module/ncs/chk_merimes_comp.ncs and b/_module/ncs/chk_merimes_comp.ncs differ diff --git a/_module/ncs/chk_merimess_1.ncs b/_module/ncs/chk_merimess_1.ncs index 220fcf95..85606d41 100644 Binary files a/_module/ncs/chk_merimess_1.ncs and b/_module/ncs/chk_merimess_1.ncs differ diff --git a/_module/ncs/chk_merimess_2.ncs b/_module/ncs/chk_merimess_2.ncs index 27a5eb2d..c8e6a3b7 100644 Binary files a/_module/ncs/chk_merimess_2.ncs and b/_module/ncs/chk_merimess_2.ncs differ diff --git a/_module/ncs/chk_merimess_3.ncs b/_module/ncs/chk_merimess_3.ncs index 699f1463..de8869f2 100644 Binary files a/_module/ncs/chk_merimess_3.ncs and b/_module/ncs/chk_merimess_3.ncs differ diff --git a/_module/ncs/chk_merimess_4.ncs b/_module/ncs/chk_merimess_4.ncs index 3fb3a157..60a1ed9c 100644 Binary files a/_module/ncs/chk_merimess_4.ncs and b/_module/ncs/chk_merimess_4.ncs differ diff --git a/_module/ncs/chk_merimess_5.ncs b/_module/ncs/chk_merimess_5.ncs index 5cb2b287..560f2b30 100644 Binary files a/_module/ncs/chk_merimess_5.ncs and b/_module/ncs/chk_merimess_5.ncs differ diff --git a/_module/ncs/chk_ygdrag_comp.ncs b/_module/ncs/chk_ygdrag_comp.ncs index afda9c2b..abecef7e 100644 Binary files a/_module/ncs/chk_ygdrag_comp.ncs and b/_module/ncs/chk_ygdrag_comp.ncs differ diff --git a/_module/ncs/chk_ygdragon_1.ncs b/_module/ncs/chk_ygdragon_1.ncs index e926c821..366d65ed 100644 Binary files a/_module/ncs/chk_ygdragon_1.ncs and b/_module/ncs/chk_ygdragon_1.ncs differ diff --git a/_module/ncs/chk_ygdragon_2.ncs b/_module/ncs/chk_ygdragon_2.ncs index de2b666b..63cdf3be 100644 Binary files a/_module/ncs/chk_ygdragon_2.ncs and b/_module/ncs/chk_ygdragon_2.ncs differ diff --git a/_module/ncs/chng_fact_hostil.ncs b/_module/ncs/chng_fact_hostil.ncs index 551363e5..0426b44a 100644 Binary files a/_module/ncs/chng_fact_hostil.ncs and b/_module/ncs/chng_fact_hostil.ncs differ diff --git a/_module/ncs/cho_god_punish.ncs b/_module/ncs/cho_god_punish.ncs index 0da37383..5f5fe142 100644 Binary files a/_module/ncs/cho_god_punish.ncs and b/_module/ncs/cho_god_punish.ncs differ diff --git a/_module/ncs/cho_int_give.ncs b/_module/ncs/cho_int_give.ncs index 245590e8..3b517c49 100644 Binary files a/_module/ncs/cho_int_give.ncs and b/_module/ncs/cho_int_give.ncs differ diff --git a/_module/ncs/cho_wis_give.ncs b/_module/ncs/cho_wis_give.ncs index 8b3fdaa6..14f23944 100644 Binary files a/_module/ncs/cho_wis_give.ncs and b/_module/ncs/cho_wis_give.ncs differ diff --git a/_module/ncs/choparing.ncs b/_module/ncs/choparing.ncs index a539e451..fd391e2b 100644 Binary files a/_module/ncs/choparing.ncs and b/_module/ncs/choparing.ncs differ diff --git a/_module/ncs/ck_10000_gp.ncs b/_module/ncs/ck_10000_gp.ncs index 0cd3b177..d981c1ef 100644 Binary files a/_module/ncs/ck_10000_gp.ncs and b/_module/ncs/ck_10000_gp.ncs differ diff --git a/_module/ncs/ck_100_gp.ncs b/_module/ncs/ck_100_gp.ncs index 42e31c4a..51a4393d 100644 Binary files a/_module/ncs/ck_100_gp.ncs and b/_module/ncs/ck_100_gp.ncs differ diff --git a/_module/ncs/ck_100k_gp.ncs b/_module/ncs/ck_100k_gp.ncs index 60c4326e..df77b6bb 100644 Binary files a/_module/ncs/ck_100k_gp.ncs and b/_module/ncs/ck_100k_gp.ncs differ diff --git a/_module/ncs/ck_1k_gp.ncs b/_module/ncs/ck_1k_gp.ncs index 628da939..330852b8 100644 Binary files a/_module/ncs/ck_1k_gp.ncs and b/_module/ncs/ck_1k_gp.ncs differ diff --git a/_module/ncs/ck_20000_gp.ncs b/_module/ncs/ck_20000_gp.ncs index ecc47cfe..1a071543 100644 Binary files a/_module/ncs/ck_20000_gp.ncs and b/_module/ncs/ck_20000_gp.ncs differ diff --git a/_module/ncs/ck_2000_gp.ncs b/_module/ncs/ck_2000_gp.ncs index 1f7cc2fb..81b18281 100644 Binary files a/_module/ncs/ck_2000_gp.ncs and b/_module/ncs/ck_2000_gp.ncs differ diff --git a/_module/ncs/ck_4_item_worn1.ncs b/_module/ncs/ck_4_item_worn1.ncs index 682738d7..805402d2 100644 Binary files a/_module/ncs/ck_4_item_worn1.ncs and b/_module/ncs/ck_4_item_worn1.ncs differ diff --git a/_module/ncs/ck_50000_gp.ncs b/_module/ncs/ck_50000_gp.ncs index 389fca50..2cafd7be 100644 Binary files a/_module/ncs/ck_50000_gp.ncs and b/_module/ncs/ck_50000_gp.ncs differ diff --git a/_module/ncs/ck_5000_gp.ncs b/_module/ncs/ck_5000_gp.ncs index d20bdc1a..b0444748 100644 Binary files a/_module/ncs/ck_5000_gp.ncs and b/_module/ncs/ck_5000_gp.ncs differ diff --git a/_module/ncs/ck_500_gp.ncs b/_module/ncs/ck_500_gp.ncs index e7a04270..e29cdffe 100644 Binary files a/_module/ncs/ck_500_gp.ncs and b/_module/ncs/ck_500_gp.ncs differ diff --git a/_module/ncs/ck_50_gp.ncs b/_module/ncs/ck_50_gp.ncs index 964e0649..0671cb62 100644 Binary files a/_module/ncs/ck_50_gp.ncs and b/_module/ncs/ck_50_gp.ncs differ diff --git a/_module/ncs/ck_bearskin.ncs b/_module/ncs/ck_bearskin.ncs index b40b3cd7..d7a01a62 100644 Binary files a/_module/ncs/ck_bearskin.ncs and b/_module/ncs/ck_bearskin.ncs differ diff --git a/_module/ncs/ck_bearskin_no.ncs b/_module/ncs/ck_bearskin_no.ncs index e58a3f84..4905455b 100644 Binary files a/_module/ncs/ck_bearskin_no.ncs and b/_module/ncs/ck_bearskin_no.ncs differ diff --git a/_module/ncs/ck_chokey.ncs b/_module/ncs/ck_chokey.ncs index 1a1d8726..9dcf5d3b 100644 Binary files a/_module/ncs/ck_chokey.ncs and b/_module/ncs/ck_chokey.ncs differ diff --git a/_module/ncs/ck_dryadboot.ncs b/_module/ncs/ck_dryadboot.ncs index 8afd49bc..70625eac 100644 Binary files a/_module/ncs/ck_dryadboot.ncs and b/_module/ncs/ck_dryadboot.ncs differ diff --git a/_module/ncs/ck_gender_girl.ncs b/_module/ncs/ck_gender_girl.ncs index b91c32dc..0230d6fd 100644 Binary files a/_module/ncs/ck_gender_girl.ncs and b/_module/ncs/ck_gender_girl.ncs differ diff --git a/_module/ncs/ck_item_wolfbane.ncs b/_module/ncs/ck_item_wolfbane.ncs index d030271f..2c3eebcf 100644 Binary files a/_module/ncs/ck_item_wolfbane.ncs and b/_module/ncs/ck_item_wolfbane.ncs differ diff --git a/_module/ncs/ck_itm_enter_kdb.ncs b/_module/ncs/ck_itm_enter_kdb.ncs index f11b0dc5..a2322c87 100644 Binary files a/_module/ncs/ck_itm_enter_kdb.ncs and b/_module/ncs/ck_itm_enter_kdb.ncs differ diff --git a/_module/ncs/ck_itm_enter_ohs.ncs b/_module/ncs/ck_itm_enter_ohs.ncs index 5498197f..8592a87b 100644 Binary files a/_module/ncs/ck_itm_enter_ohs.ncs and b/_module/ncs/ck_itm_enter_ohs.ncs differ diff --git a/_module/ncs/ck_lucy_done.ncs b/_module/ncs/ck_lucy_done.ncs index df90bc02..7172e2c2 100644 Binary files a/_module/ncs/ck_lucy_done.ncs and b/_module/ncs/ck_lucy_done.ncs differ diff --git a/_module/ncs/ck_ohs_key_gypo.ncs b/_module/ncs/ck_ohs_key_gypo.ncs index b809dead..e782162e 100644 Binary files a/_module/ncs/ck_ohs_key_gypo.ncs and b/_module/ncs/ck_ohs_key_gypo.ncs differ diff --git a/_module/ncs/ck_pirate_kdbkey.ncs b/_module/ncs/ck_pirate_kdbkey.ncs index 8c5f3d2e..f302d654 100644 Binary files a/_module/ncs/ck_pirate_kdbkey.ncs and b/_module/ncs/ck_pirate_kdbkey.ncs differ diff --git a/_module/ncs/ck_witchammy.ncs b/_module/ncs/ck_witchammy.ncs index 4e969ed8..d9774bef 100644 Binary files a/_module/ncs/ck_witchammy.ncs and b/_module/ncs/ck_witchammy.ncs differ diff --git a/_module/ncs/ck_wolfkey.ncs b/_module/ncs/ck_wolfkey.ncs index 6f872f75..d94dba94 100644 Binary files a/_module/ncs/ck_wolfkey.ncs and b/_module/ncs/ck_wolfkey.ncs differ diff --git a/_module/ncs/clear_stores_out.ncs b/_module/ncs/clear_stores_out.ncs index 804a2c73..84167987 100644 Binary files a/_module/ncs/clear_stores_out.ncs and b/_module/ncs/clear_stores_out.ncs differ diff --git a/_module/ncs/cleric_ck.ncs b/_module/ncs/cleric_ck.ncs index 5b625121..cdd23e7b 100644 Binary files a/_module/ncs/cleric_ck.ncs and b/_module/ncs/cleric_ck.ncs differ diff --git a/_module/ncs/clone_test_ring3.ncs b/_module/ncs/clone_test_ring3.ncs index 10456838..28193df2 100644 Binary files a/_module/ncs/clone_test_ring3.ncs and b/_module/ncs/clone_test_ring3.ncs differ diff --git a/_module/ncs/combat_dummy.ncs b/_module/ncs/combat_dummy.ncs index 4de3f163..9ac08437 100644 Binary files a/_module/ncs/combat_dummy.ncs and b/_module/ncs/combat_dummy.ncs differ diff --git a/_module/ncs/combatrndcreatur.ncs b/_module/ncs/combatrndcreatur.ncs index 6911d9e4..4691cb00 100644 Binary files a/_module/ncs/combatrndcreatur.ncs and b/_module/ncs/combatrndcreatur.ncs differ diff --git a/_module/ncs/convo_ck_lev10.ncs b/_module/ncs/convo_ck_lev10.ncs index e11f2da1..146d7f24 100644 Binary files a/_module/ncs/convo_ck_lev10.ncs and b/_module/ncs/convo_ck_lev10.ncs differ diff --git a/_module/ncs/convo_script_sit.ncs b/_module/ncs/convo_script_sit.ncs index d4dd36f1..3ca062ab 100644 Binary files a/_module/ncs/convo_script_sit.ncs and b/_module/ncs/convo_script_sit.ncs differ diff --git a/_module/ncs/cow_ring.ncs b/_module/ncs/cow_ring.ncs index 70ed93a3..ad8966f5 100644 Binary files a/_module/ncs/cow_ring.ncs and b/_module/ncs/cow_ring.ncs differ diff --git a/_module/ncs/createbastardswd.ncs b/_module/ncs/createbastardswd.ncs index 486ca982..77eae1a1 100644 Binary files a/_module/ncs/createbastardswd.ncs and b/_module/ncs/createbastardswd.ncs differ diff --git a/_module/ncs/createbattleaxe.ncs b/_module/ncs/createbattleaxe.ncs index 2a37cfa6..508ba28c 100644 Binary files a/_module/ncs/createbattleaxe.ncs and b/_module/ncs/createbattleaxe.ncs differ diff --git a/_module/ncs/createdagger.ncs b/_module/ncs/createdagger.ncs index 3af51e99..d6f3f32f 100644 Binary files a/_module/ncs/createdagger.ncs and b/_module/ncs/createdagger.ncs differ diff --git a/_module/ncs/createdblaxe.ncs b/_module/ncs/createdblaxe.ncs index ac940732..7bc98061 100644 Binary files a/_module/ncs/createdblaxe.ncs and b/_module/ncs/createdblaxe.ncs differ diff --git a/_module/ncs/createdblscimita.ncs b/_module/ncs/createdblscimita.ncs index d4bde537..016805ba 100644 Binary files a/_module/ncs/createdblscimita.ncs and b/_module/ncs/createdblscimita.ncs differ diff --git a/_module/ncs/creatediremace.ncs b/_module/ncs/creatediremace.ncs index e673768f..35a51dc7 100644 Binary files a/_module/ncs/creatediremace.ncs and b/_module/ncs/creatediremace.ncs differ diff --git a/_module/ncs/createdwarvenaxe.ncs b/_module/ncs/createdwarvenaxe.ncs index 7ed42bef..38b7bc24 100644 Binary files a/_module/ncs/createdwarvenaxe.ncs and b/_module/ncs/createdwarvenaxe.ncs differ diff --git a/_module/ncs/createeagleclaw.ncs b/_module/ncs/createeagleclaw.ncs index c50761f1..2cdc8cf2 100644 Binary files a/_module/ncs/createeagleclaw.ncs and b/_module/ncs/createeagleclaw.ncs differ diff --git a/_module/ncs/createelvencourt.ncs b/_module/ncs/createelvencourt.ncs index 6e62f6b8..fe776cfe 100644 Binary files a/_module/ncs/createelvencourt.ncs and b/_module/ncs/createelvencourt.ncs differ diff --git a/_module/ncs/createelvenlight.ncs b/_module/ncs/createelvenlight.ncs index 920797b0..de6fe237 100644 Binary files a/_module/ncs/createelvenlight.ncs and b/_module/ncs/createelvenlight.ncs differ diff --git a/_module/ncs/createelventhinb.ncs b/_module/ncs/createelventhinb.ncs index 1fb40855..5d8adad8 100644 Binary files a/_module/ncs/createelventhinb.ncs and b/_module/ncs/createelventhinb.ncs differ diff --git a/_module/ncs/createfalchion.ncs b/_module/ncs/createfalchion.ncs index 8dd29a6d..0f7d819b 100644 Binary files a/_module/ncs/createfalchion.ncs and b/_module/ncs/createfalchion.ncs differ diff --git a/_module/ncs/creategauntlet.ncs b/_module/ncs/creategauntlet.ncs index 2e73464e..00264d2e 100644 Binary files a/_module/ncs/creategauntlet.ncs and b/_module/ncs/creategauntlet.ncs differ diff --git a/_module/ncs/creategoad.ncs b/_module/ncs/creategoad.ncs index 4ef29803..e5b9fbec 100644 Binary files a/_module/ncs/creategoad.ncs and b/_module/ncs/creategoad.ncs differ diff --git a/_module/ncs/creategreataxe.ncs b/_module/ncs/creategreataxe.ncs index 20afba00..8294e451 100644 Binary files a/_module/ncs/creategreataxe.ncs and b/_module/ncs/creategreataxe.ncs differ diff --git a/_module/ncs/creategreatsword.ncs b/_module/ncs/creategreatsword.ncs index bce34a69..dcabd1a7 100644 Binary files a/_module/ncs/creategreatsword.ncs and b/_module/ncs/creategreatsword.ncs differ diff --git a/_module/ncs/createhalberd.ncs b/_module/ncs/createhalberd.ncs index ca0ff34a..a37cbebb 100644 Binary files a/_module/ncs/createhalberd.ncs and b/_module/ncs/createhalberd.ncs differ diff --git a/_module/ncs/createhandaxe.ncs b/_module/ncs/createhandaxe.ncs index 7aaf0bbb..f4318e4f 100644 Binary files a/_module/ncs/createhandaxe.ncs and b/_module/ncs/createhandaxe.ncs differ diff --git a/_module/ncs/createheavyflail.ncs b/_module/ncs/createheavyflail.ncs index 829e90c9..39dd01bc 100644 Binary files a/_module/ncs/createheavyflail.ncs and b/_module/ncs/createheavyflail.ncs differ diff --git a/_module/ncs/createheavymace.ncs b/_module/ncs/createheavymace.ncs index 27432fe3..4129bfed 100644 Binary files a/_module/ncs/createheavymace.ncs and b/_module/ncs/createheavymace.ncs differ diff --git a/_module/ncs/createheavypick.ncs b/_module/ncs/createheavypick.ncs index c3c87c29..88fa8e75 100644 Binary files a/_module/ncs/createheavypick.ncs and b/_module/ncs/createheavypick.ncs differ diff --git a/_module/ncs/createhvycrossbo.ncs b/_module/ncs/createhvycrossbo.ncs index dad4930c..084853aa 100644 Binary files a/_module/ncs/createhvycrossbo.ncs and b/_module/ncs/createhvycrossbo.ncs differ diff --git a/_module/ncs/createhvycrssbo2.ncs b/_module/ncs/createhvycrssbo2.ncs index 62e41b2f..2a64f600 100644 Binary files a/_module/ncs/createhvycrssbo2.ncs and b/_module/ncs/createhvycrssbo2.ncs differ diff --git a/_module/ncs/createkama.ncs b/_module/ncs/createkama.ncs index c00a0f27..b8cb57f7 100644 Binary files a/_module/ncs/createkama.ncs and b/_module/ncs/createkama.ncs differ diff --git a/_module/ncs/createkatana.ncs b/_module/ncs/createkatana.ncs index bda0e3a8..28c61bd6 100644 Binary files a/_module/ncs/createkatana.ncs and b/_module/ncs/createkatana.ncs differ diff --git a/_module/ncs/createkatar.ncs b/_module/ncs/createkatar.ncs index 2026ce53..34c4fccd 100644 Binary files a/_module/ncs/createkatar.ncs and b/_module/ncs/createkatar.ncs differ diff --git a/_module/ncs/createkukri.ncs b/_module/ncs/createkukri.ncs index d43c0b6e..69aa1b56 100644 Binary files a/_module/ncs/createkukri.ncs and b/_module/ncs/createkukri.ncs differ diff --git a/_module/ncs/createlance.ncs b/_module/ncs/createlance.ncs index a5ec89f2..2bb731e8 100644 Binary files a/_module/ncs/createlance.ncs and b/_module/ncs/createlance.ncs differ diff --git a/_module/ncs/createlightflail.ncs b/_module/ncs/createlightflail.ncs index 7f79ea05..bbf28d15 100644 Binary files a/_module/ncs/createlightflail.ncs and b/_module/ncs/createlightflail.ncs differ diff --git a/_module/ncs/createlightpick.ncs b/_module/ncs/createlightpick.ncs index 30448cc0..b7672233 100644 Binary files a/_module/ncs/createlightpick.ncs and b/_module/ncs/createlightpick.ncs differ diff --git a/_module/ncs/createlitexbow.ncs b/_module/ncs/createlitexbow.ncs index 8502961b..fd5815ae 100644 Binary files a/_module/ncs/createlitexbow.ncs and b/_module/ncs/createlitexbow.ncs differ diff --git a/_module/ncs/createlitexbow2.ncs b/_module/ncs/createlitexbow2.ncs index 2d3906d8..48895213 100644 Binary files a/_module/ncs/createlitexbow2.ncs and b/_module/ncs/createlitexbow2.ncs differ diff --git a/_module/ncs/createlongbow.ncs b/_module/ncs/createlongbow.ncs index 93538325..4717ab6d 100644 Binary files a/_module/ncs/createlongbow.ncs and b/_module/ncs/createlongbow.ncs differ diff --git a/_module/ncs/createlongbow2.ncs b/_module/ncs/createlongbow2.ncs index 88473900..c2c3cfb0 100644 Binary files a/_module/ncs/createlongbow2.ncs and b/_module/ncs/createlongbow2.ncs differ diff --git a/_module/ncs/createlongsword.ncs b/_module/ncs/createlongsword.ncs index 01e4248b..5beab58f 100644 Binary files a/_module/ncs/createlongsword.ncs and b/_module/ncs/createlongsword.ncs differ diff --git a/_module/ncs/createmace.ncs b/_module/ncs/createmace.ncs index 1a8073f8..bf8c2cbb 100644 Binary files a/_module/ncs/createmace.ncs and b/_module/ncs/createmace.ncs differ diff --git a/_module/ncs/createmaul.ncs b/_module/ncs/createmaul.ncs index a56760e0..a94cbe20 100644 Binary files a/_module/ncs/createmaul.ncs and b/_module/ncs/createmaul.ncs differ diff --git a/_module/ncs/createmorningsta.ncs b/_module/ncs/createmorningsta.ncs index cb4e258e..8669fa97 100644 Binary files a/_module/ncs/createmorningsta.ncs and b/_module/ncs/createmorningsta.ncs differ diff --git a/_module/ncs/createnunchaku.ncs b/_module/ncs/createnunchaku.ncs index 738aef2f..614e5153 100644 Binary files a/_module/ncs/createnunchaku.ncs and b/_module/ncs/createnunchaku.ncs differ diff --git a/_module/ncs/createquartersta.ncs b/_module/ncs/createquartersta.ncs index bfcebab2..9ecd201a 100644 Binary files a/_module/ncs/createquartersta.ncs and b/_module/ncs/createquartersta.ncs differ diff --git a/_module/ncs/createrapier.ncs b/_module/ncs/createrapier.ncs index 271f02d9..8673a6f2 100644 Binary files a/_module/ncs/createrapier.ncs and b/_module/ncs/createrapier.ncs differ diff --git a/_module/ncs/createsai.ncs b/_module/ncs/createsai.ncs index 9c28d9cb..485963ca 100644 Binary files a/_module/ncs/createsai.ncs and b/_module/ncs/createsai.ncs differ diff --git a/_module/ncs/createsap.ncs b/_module/ncs/createsap.ncs index 5654956d..6f337ec7 100644 Binary files a/_module/ncs/createsap.ncs and b/_module/ncs/createsap.ncs differ diff --git a/_module/ncs/createscimitar.ncs b/_module/ncs/createscimitar.ncs index 33fddd1c..aad01641 100644 Binary files a/_module/ncs/createscimitar.ncs and b/_module/ncs/createscimitar.ncs differ diff --git a/_module/ncs/createscythe.ncs b/_module/ncs/createscythe.ncs index 65ede0e1..00ccafb2 100644 Binary files a/_module/ncs/createscythe.ncs and b/_module/ncs/createscythe.ncs differ diff --git a/_module/ncs/createshortbow.ncs b/_module/ncs/createshortbow.ncs index 2cdc73bf..8849a21b 100644 Binary files a/_module/ncs/createshortbow.ncs and b/_module/ncs/createshortbow.ncs differ diff --git a/_module/ncs/createshortbow2.ncs b/_module/ncs/createshortbow2.ncs index da928f79..e09e5eb1 100644 Binary files a/_module/ncs/createshortbow2.ncs and b/_module/ncs/createshortbow2.ncs differ diff --git a/_module/ncs/createshortswrd.ncs b/_module/ncs/createshortswrd.ncs index 396d8c84..20a552e0 100644 Binary files a/_module/ncs/createshortswrd.ncs and b/_module/ncs/createshortswrd.ncs differ diff --git a/_module/ncs/createsickle.ncs b/_module/ncs/createsickle.ncs index c50a3ee9..1927fd1a 100644 Binary files a/_module/ncs/createsickle.ncs and b/_module/ncs/createsickle.ncs differ diff --git a/_module/ncs/createsling.ncs b/_module/ncs/createsling.ncs index 45aab22a..4c95eee3 100644 Binary files a/_module/ncs/createsling.ncs and b/_module/ncs/createsling.ncs differ diff --git a/_module/ncs/createsling2.ncs b/_module/ncs/createsling2.ncs index 8eedcb68..274cd785 100644 Binary files a/_module/ncs/createsling2.ncs and b/_module/ncs/createsling2.ncs differ diff --git a/_module/ncs/createspear.ncs b/_module/ncs/createspear.ncs index de3ccaef..7a4a4830 100644 Binary files a/_module/ncs/createspear.ncs and b/_module/ncs/createspear.ncs differ diff --git a/_module/ncs/createtrident.ncs b/_module/ncs/createtrident.ncs index 816e2663..e5837e25 100644 Binary files a/_module/ncs/createtrident.ncs and b/_module/ncs/createtrident.ncs differ diff --git a/_module/ncs/createtwobldswrd.ncs b/_module/ncs/createtwobldswrd.ncs index 177594e3..5b00cef9 100644 Binary files a/_module/ncs/createtwobldswrd.ncs and b/_module/ncs/createtwobldswrd.ncs differ diff --git a/_module/ncs/createwarhammer.ncs b/_module/ncs/createwarhammer.ncs index 8d83935a..7fdd71fe 100644 Binary files a/_module/ncs/createwarhammer.ncs and b/_module/ncs/createwarhammer.ncs differ diff --git a/_module/ncs/createwhip.ncs b/_module/ncs/createwhip.ncs index 8411e296..c4c580a4 100644 Binary files a/_module/ncs/createwhip.ncs and b/_module/ncs/createwhip.ncs differ diff --git a/_module/ncs/croydecaveyells1.ncs b/_module/ncs/croydecaveyells1.ncs index 70a01736..bbbdddf2 100644 Binary files a/_module/ncs/croydecaveyells1.ncs and b/_module/ncs/croydecaveyells1.ncs differ diff --git a/_module/ncs/croydecaveyells2.ncs b/_module/ncs/croydecaveyells2.ncs index 7aa54687..a7a460b3 100644 Binary files a/_module/ncs/croydecaveyells2.ncs and b/_module/ncs/croydecaveyells2.ncs differ diff --git a/_module/ncs/cugel_done.ncs b/_module/ncs/cugel_done.ncs index 493ddadf..81327b2b 100644 Binary files a/_module/ncs/cugel_done.ncs and b/_module/ncs/cugel_done.ncs differ diff --git a/_module/ncs/cugel_riftstone.ncs b/_module/ncs/cugel_riftstone.ncs index ee42f6ec..7f7f4ca6 100644 Binary files a/_module/ncs/cugel_riftstone.ncs and b/_module/ncs/cugel_riftstone.ncs differ diff --git a/_module/ncs/cugel_set_var.ncs b/_module/ncs/cugel_set_var.ncs index 333408eb..ba60fb48 100644 Binary files a/_module/ncs/cugel_set_var.ncs and b/_module/ncs/cugel_set_var.ncs differ diff --git a/_module/ncs/cugel_spoke2.ncs b/_module/ncs/cugel_spoke2.ncs index 49adc743..11a43294 100644 Binary files a/_module/ncs/cugel_spoke2.ncs and b/_module/ncs/cugel_spoke2.ncs differ diff --git a/_module/ncs/cutsceneghost.ncs b/_module/ncs/cutsceneghost.ncs index 1d7e0218..3f68bd8e 100644 Binary files a/_module/ncs/cutsceneghost.ncs and b/_module/ncs/cutsceneghost.ncs differ diff --git a/_module/ncs/cutsceneinvisibl.ncs b/_module/ncs/cutsceneinvisibl.ncs index cd8189de..0d11e8a0 100644 Binary files a/_module/ncs/cutsceneinvisibl.ncs and b/_module/ncs/cutsceneinvisibl.ncs differ diff --git a/_module/ncs/cv_rest_arealist.ncs b/_module/ncs/cv_rest_arealist.ncs index 366d4f04..cea6994e 100644 Binary files a/_module/ncs/cv_rest_arealist.ncs and b/_module/ncs/cv_rest_arealist.ncs differ diff --git a/_module/ncs/dante_letodel_at.ncs b/_module/ncs/dante_letodel_at.ncs index 2adc4b42..fbb1cd59 100644 Binary files a/_module/ncs/dante_letodel_at.ncs and b/_module/ncs/dante_letodel_at.ncs differ diff --git a/_module/ncs/dead_magic_enter.ncs b/_module/ncs/dead_magic_enter.ncs index 4ae2e379..a6303271 100644 Binary files a/_module/ncs/dead_magic_enter.ncs and b/_module/ncs/dead_magic_enter.ncs differ diff --git a/_module/ncs/dead_magic_exit.ncs b/_module/ncs/dead_magic_exit.ncs index 2008fb69..f8932bb8 100644 Binary files a/_module/ncs/dead_magic_exit.ncs and b/_module/ncs/dead_magic_exit.ncs differ diff --git a/_module/ncs/death_stat_spawn.ncs b/_module/ncs/death_stat_spawn.ncs index 2e981efc..65609458 100644 Binary files a/_module/ncs/death_stat_spawn.ncs and b/_module/ncs/death_stat_spawn.ncs differ diff --git a/_module/ncs/death_vikinglead.ncs b/_module/ncs/death_vikinglead.ncs index 4f9eebd7..521d2c96 100644 Binary files a/_module/ncs/death_vikinglead.ncs and b/_module/ncs/death_vikinglead.ncs differ diff --git a/_module/ncs/death_xp1000.ncs b/_module/ncs/death_xp1000.ncs index 22ef7916..4a3f1e26 100644 Binary files a/_module/ncs/death_xp1000.ncs and b/_module/ncs/death_xp1000.ncs differ diff --git a/_module/ncs/deathstatu_spawn.ncs b/_module/ncs/deathstatu_spawn.ncs index 69a18ceb..abca6f94 100644 Binary files a/_module/ncs/deathstatu_spawn.ncs and b/_module/ncs/deathstatu_spawn.ncs differ diff --git a/_module/ncs/delay_enter.ncs b/_module/ncs/delay_enter.ncs index 203bdc05..2ba7a1ee 100644 Binary files a/_module/ncs/delay_enter.ncs and b/_module/ncs/delay_enter.ncs differ diff --git a/_module/ncs/delay_on_exit.ncs b/_module/ncs/delay_on_exit.ncs index 239f880e..831a1157 100644 Binary files a/_module/ncs/delay_on_exit.ncs and b/_module/ncs/delay_on_exit.ncs differ diff --git a/_module/ncs/delete1.ncs b/_module/ncs/delete1.ncs index c45cd1f4..ff68cbe8 100644 Binary files a/_module/ncs/delete1.ncs and b/_module/ncs/delete1.ncs differ diff --git a/_module/ncs/delete3.ncs b/_module/ncs/delete3.ncs index 6866cbba..37891f93 100644 Binary files a/_module/ncs/delete3.ncs and b/_module/ncs/delete3.ncs differ diff --git a/_module/ncs/deronportal.ncs b/_module/ncs/deronportal.ncs index facef8c8..4c5a8357 100644 Binary files a/_module/ncs/deronportal.ncs and b/_module/ncs/deronportal.ncs differ diff --git a/_module/ncs/deronportal2.ncs b/_module/ncs/deronportal2.ncs index da4f76ee..5a1a58c9 100644 Binary files a/_module/ncs/deronportal2.ncs and b/_module/ncs/deronportal2.ncs differ diff --git a/_module/ncs/deronrecalldevic.ncs b/_module/ncs/deronrecalldevic.ncs index 885cceb7..14eb8315 100644 Binary files a/_module/ncs/deronrecalldevic.ncs and b/_module/ncs/deronrecalldevic.ncs differ diff --git a/_module/ncs/deronsafe.ncs b/_module/ncs/deronsafe.ncs index dab54180..720885c9 100644 Binary files a/_module/ncs/deronsafe.ncs and b/_module/ncs/deronsafe.ncs differ diff --git a/_module/ncs/deronsafeintchk.ncs b/_module/ncs/deronsafeintchk.ncs index 9441f8c0..24ee4af7 100644 Binary files a/_module/ncs/deronsafeintchk.ncs and b/_module/ncs/deronsafeintchk.ncs differ diff --git a/_module/ncs/deronunsafeintck.ncs b/_module/ncs/deronunsafeintck.ncs index 29487950..e13cda92 100644 Binary files a/_module/ncs/deronunsafeintck.ncs and b/_module/ncs/deronunsafeintck.ncs differ diff --git a/_module/ncs/desertheat2.ncs b/_module/ncs/desertheat2.ncs index 276929f3..1203a8fc 100644 Binary files a/_module/ncs/desertheat2.ncs and b/_module/ncs/desertheat2.ncs differ diff --git a/_module/ncs/detect_glow_evil.ncs b/_module/ncs/detect_glow_evil.ncs index 593a8d5b..f154d2bb 100644 Binary files a/_module/ncs/detect_glow_evil.ncs and b/_module/ncs/detect_glow_evil.ncs differ diff --git a/_module/ncs/dh2_onaenter.ncs b/_module/ncs/dh2_onaenter.ncs index 2bd6697d..83ccb87c 100644 Binary files a/_module/ncs/dh2_onaenter.ncs and b/_module/ncs/dh2_onaenter.ncs differ diff --git a/_module/ncs/dh2_trigenter.ncs b/_module/ncs/dh2_trigenter.ncs index acf23068..4cdd05e1 100644 Binary files a/_module/ncs/dh2_trigenter.ncs and b/_module/ncs/dh2_trigenter.ncs differ diff --git a/_module/ncs/dh2_trigexit.ncs b/_module/ncs/dh2_trigexit.ncs index 44e057e2..a3d39459 100644 Binary files a/_module/ncs/dh2_trigexit.ncs and b/_module/ncs/dh2_trigexit.ncs differ diff --git a/_module/ncs/distancecheck2.ncs b/_module/ncs/distancecheck2.ncs index 8e36ebd0..084b3b85 100644 Binary files a/_module/ncs/distancecheck2.ncs and b/_module/ncs/distancecheck2.ncs differ diff --git a/_module/ncs/distancecheck4.ncs b/_module/ncs/distancecheck4.ncs index 7d2c07ee..5177c55a 100644 Binary files a/_module/ncs/distancecheck4.ncs and b/_module/ncs/distancecheck4.ncs differ diff --git a/_module/ncs/distancecheck6.ncs b/_module/ncs/distancecheck6.ncs index d9b0d188..d74a5cd4 100644 Binary files a/_module/ncs/distancecheck6.ncs and b/_module/ncs/distancecheck6.ncs differ diff --git a/_module/ncs/dm_token_spawn.ncs b/_module/ncs/dm_token_spawn.ncs index ec868a1c..a6884135 100644 Binary files a/_module/ncs/dm_token_spawn.ncs and b/_module/ncs/dm_token_spawn.ncs differ diff --git a/_module/ncs/dmshelper.ncs b/_module/ncs/dmshelper.ncs index 95983014..fe24f4b4 100644 Binary files a/_module/ncs/dmshelper.ncs and b/_module/ncs/dmshelper.ncs differ diff --git a/_module/ncs/dmw_conv_end.ncs b/_module/ncs/dmw_conv_end.ncs index a38e2703..746d3c9a 100644 Binary files a/_module/ncs/dmw_conv_end.ncs and b/_module/ncs/dmw_conv_end.ncs differ diff --git a/_module/ncs/dmw_do_dialog1.ncs b/_module/ncs/dmw_do_dialog1.ncs index 084b2478..df4fb9c1 100644 Binary files a/_module/ncs/dmw_do_dialog1.ncs and b/_module/ncs/dmw_do_dialog1.ncs differ diff --git a/_module/ncs/dmw_do_dialog2.ncs b/_module/ncs/dmw_do_dialog2.ncs index 834e2973..ea2f2010 100644 Binary files a/_module/ncs/dmw_do_dialog2.ncs and b/_module/ncs/dmw_do_dialog2.ncs differ diff --git a/_module/ncs/dmw_do_dialog3.ncs b/_module/ncs/dmw_do_dialog3.ncs index 48c50713..63ada2ce 100644 Binary files a/_module/ncs/dmw_do_dialog3.ncs and b/_module/ncs/dmw_do_dialog3.ncs differ diff --git a/_module/ncs/dmw_do_dialog4.ncs b/_module/ncs/dmw_do_dialog4.ncs index 85b752c0..39870672 100644 Binary files a/_module/ncs/dmw_do_dialog4.ncs and b/_module/ncs/dmw_do_dialog4.ncs differ diff --git a/_module/ncs/dmw_do_dialog5.ncs b/_module/ncs/dmw_do_dialog5.ncs index a88fd6eb..e8187e10 100644 Binary files a/_module/ncs/dmw_do_dialog5.ncs and b/_module/ncs/dmw_do_dialog5.ncs differ diff --git a/_module/ncs/dmw_do_dialog6.ncs b/_module/ncs/dmw_do_dialog6.ncs index 9eb6c1f7..5c00a79d 100644 Binary files a/_module/ncs/dmw_do_dialog6.ncs and b/_module/ncs/dmw_do_dialog6.ncs differ diff --git a/_module/ncs/dmw_do_dialog7.ncs b/_module/ncs/dmw_do_dialog7.ncs index 2c3485a3..32653232 100644 Binary files a/_module/ncs/dmw_do_dialog7.ncs and b/_module/ncs/dmw_do_dialog7.ncs differ diff --git a/_module/ncs/dmw_do_dialog8.ncs b/_module/ncs/dmw_do_dialog8.ncs index 51b35ba1..c2a560e0 100644 Binary files a/_module/ncs/dmw_do_dialog8.ncs and b/_module/ncs/dmw_do_dialog8.ncs differ diff --git a/_module/ncs/dmw_do_dialog9.ncs b/_module/ncs/dmw_do_dialog9.ncs index 6441c04f..10134967 100644 Binary files a/_module/ncs/dmw_do_dialog9.ncs and b/_module/ncs/dmw_do_dialog9.ncs differ diff --git a/_module/ncs/dmw_test_dialog0.ncs b/_module/ncs/dmw_test_dialog0.ncs index 077df9b4..fb5d6fa4 100644 Binary files a/_module/ncs/dmw_test_dialog0.ncs and b/_module/ncs/dmw_test_dialog0.ncs differ diff --git a/_module/ncs/dmw_test_dialog1.ncs b/_module/ncs/dmw_test_dialog1.ncs index 2db20fdf..43299f4d 100644 Binary files a/_module/ncs/dmw_test_dialog1.ncs and b/_module/ncs/dmw_test_dialog1.ncs differ diff --git a/_module/ncs/dmw_test_dialog2.ncs b/_module/ncs/dmw_test_dialog2.ncs index aba01edb..cdd15874 100644 Binary files a/_module/ncs/dmw_test_dialog2.ncs and b/_module/ncs/dmw_test_dialog2.ncs differ diff --git a/_module/ncs/dmw_test_dialog3.ncs b/_module/ncs/dmw_test_dialog3.ncs index 6b9da4c1..c9f6f028 100644 Binary files a/_module/ncs/dmw_test_dialog3.ncs and b/_module/ncs/dmw_test_dialog3.ncs differ diff --git a/_module/ncs/dmw_test_dialog4.ncs b/_module/ncs/dmw_test_dialog4.ncs index 0b8d9e9d..afe22e7a 100644 Binary files a/_module/ncs/dmw_test_dialog4.ncs and b/_module/ncs/dmw_test_dialog4.ncs differ diff --git a/_module/ncs/dmw_test_dialog5.ncs b/_module/ncs/dmw_test_dialog5.ncs index eb43e785..2283d0a2 100644 Binary files a/_module/ncs/dmw_test_dialog5.ncs and b/_module/ncs/dmw_test_dialog5.ncs differ diff --git a/_module/ncs/dmw_test_dialog6.ncs b/_module/ncs/dmw_test_dialog6.ncs index 5c6626eb..7852edd7 100644 Binary files a/_module/ncs/dmw_test_dialog6.ncs and b/_module/ncs/dmw_test_dialog6.ncs differ diff --git a/_module/ncs/dmw_test_dialog7.ncs b/_module/ncs/dmw_test_dialog7.ncs index dd3a270a..00abd044 100644 Binary files a/_module/ncs/dmw_test_dialog7.ncs and b/_module/ncs/dmw_test_dialog7.ncs differ diff --git a/_module/ncs/dmw_test_dialog8.ncs b/_module/ncs/dmw_test_dialog8.ncs index b69edaa4..03b750bc 100644 Binary files a/_module/ncs/dmw_test_dialog8.ncs and b/_module/ncs/dmw_test_dialog8.ncs differ diff --git a/_module/ncs/dmw_test_dialog9.ncs b/_module/ncs/dmw_test_dialog9.ncs index fde27c0c..92204a6a 100644 Binary files a/_module/ncs/dmw_test_dialog9.ncs and b/_module/ncs/dmw_test_dialog9.ncs differ diff --git a/_module/ncs/dragon_ckvar_1.ncs b/_module/ncs/dragon_ckvar_1.ncs index 3e37f99f..f3d9d296 100644 Binary files a/_module/ncs/dragon_ckvar_1.ncs and b/_module/ncs/dragon_ckvar_1.ncs differ diff --git a/_module/ncs/dragon_ckvar_2.ncs b/_module/ncs/dragon_ckvar_2.ncs index 3f2215bf..de43f5bf 100644 Binary files a/_module/ncs/dragon_ckvar_2.ncs and b/_module/ncs/dragon_ckvar_2.ncs differ diff --git a/_module/ncs/dragon_convo_ck.ncs b/_module/ncs/dragon_convo_ck.ncs index 0305c547..bc3e550a 100644 Binary files a/_module/ncs/dragon_convo_ck.ncs and b/_module/ncs/dragon_convo_ck.ncs differ diff --git a/_module/ncs/dragon_enter21.ncs b/_module/ncs/dragon_enter21.ncs index 0e10a364..405a7d0c 100644 Binary files a/_module/ncs/dragon_enter21.ncs and b/_module/ncs/dragon_enter21.ncs differ diff --git a/_module/ncs/dragon_exit12.ncs b/_module/ncs/dragon_exit12.ncs index c509955c..2f4bd22a 100644 Binary files a/_module/ncs/dragon_exit12.ncs and b/_module/ncs/dragon_exit12.ncs differ diff --git a/_module/ncs/dragon_flyaway2.ncs b/_module/ncs/dragon_flyaway2.ncs index 1108e8f6..2b11ea52 100644 Binary files a/_module/ncs/dragon_flyaway2.ncs and b/_module/ncs/dragon_flyaway2.ncs differ diff --git a/_module/ncs/dragon_g_xp2_t_i.ncs b/_module/ncs/dragon_g_xp2_t_i.ncs index 25ac4728..5b4be5b0 100644 Binary files a/_module/ncs/dragon_g_xp2_t_i.ncs and b/_module/ncs/dragon_g_xp2_t_i.ncs differ diff --git a/_module/ncs/dragon_g_xp_t_it.ncs b/_module/ncs/dragon_g_xp_t_it.ncs index c42a09e1..bdd61505 100644 Binary files a/_module/ncs/dragon_g_xp_t_it.ncs and b/_module/ncs/dragon_g_xp_t_it.ncs differ diff --git a/_module/ncs/dragon_remove.ncs b/_module/ncs/dragon_remove.ncs index 83213e89..31f418cc 100644 Binary files a/_module/ncs/dragon_remove.ncs and b/_module/ncs/dragon_remove.ncs differ diff --git a/_module/ncs/dragoncall_enter.ncs b/_module/ncs/dragoncall_enter.ncs index 7d9feaf2..6a3895b7 100644 Binary files a/_module/ncs/dragoncall_enter.ncs and b/_module/ncs/dragoncall_enter.ncs differ diff --git a/_module/ncs/dragonspawn.ncs b/_module/ncs/dragonspawn.ncs index 655f804f..e1382116 100644 Binary files a/_module/ncs/dragonspawn.ncs and b/_module/ncs/dragonspawn.ncs differ diff --git a/_module/ncs/dragonspawn2.ncs b/_module/ncs/dragonspawn2.ncs index b25b4e4c..6ae389c4 100644 Binary files a/_module/ncs/dragonspawn2.ncs and b/_module/ncs/dragonspawn2.ncs differ diff --git a/_module/ncs/dragonsreward.ncs b/_module/ncs/dragonsreward.ncs index 73379dc5..bfb9a1cd 100644 Binary files a/_module/ncs/dragonsreward.ncs and b/_module/ncs/dragonsreward.ncs differ diff --git a/_module/ncs/drop_ondeath_npc.ncs b/_module/ncs/drop_ondeath_npc.ncs index c31b3618..3f2f5067 100644 Binary files a/_module/ncs/drop_ondeath_npc.ncs and b/_module/ncs/drop_ondeath_npc.ncs differ diff --git a/_module/ncs/drow_bell_ring.ncs b/_module/ncs/drow_bell_ring.ncs index f56e5c48..2b35e04f 100644 Binary files a/_module/ncs/drow_bell_ring.ncs and b/_module/ncs/drow_bell_ring.ncs differ diff --git a/_module/ncs/drow_ck_race.ncs b/_module/ncs/drow_ck_race.ncs index 3f065616..35317473 100644 Binary files a/_module/ncs/drow_ck_race.ncs and b/_module/ncs/drow_ck_race.ncs differ diff --git a/_module/ncs/drow_dm_getvar1.ncs b/_module/ncs/drow_dm_getvar1.ncs index 6320f39c..4cef5e4e 100644 Binary files a/_module/ncs/drow_dm_getvar1.ncs and b/_module/ncs/drow_dm_getvar1.ncs differ diff --git a/_module/ncs/drow_dm_getvar2.ncs b/_module/ncs/drow_dm_getvar2.ncs index efc72a57..1ddfffe6 100644 Binary files a/_module/ncs/drow_dm_getvar2.ncs and b/_module/ncs/drow_dm_getvar2.ncs differ diff --git a/_module/ncs/drow_dm_getvar3.ncs b/_module/ncs/drow_dm_getvar3.ncs index 0444ca5f..dc788966 100644 Binary files a/_module/ncs/drow_dm_getvar3.ncs and b/_module/ncs/drow_dm_getvar3.ncs differ diff --git a/_module/ncs/drow_dm_getvar4.ncs b/_module/ncs/drow_dm_getvar4.ncs index decb0658..36a129ce 100644 Binary files a/_module/ncs/drow_dm_getvar4.ncs and b/_module/ncs/drow_dm_getvar4.ncs differ diff --git a/_module/ncs/drow_explosion.ncs b/_module/ncs/drow_explosion.ncs index 40ab35e6..3e171678 100644 Binary files a/_module/ncs/drow_explosion.ncs and b/_module/ncs/drow_explosion.ncs differ diff --git a/_module/ncs/drow_merc_ck.ncs b/_module/ncs/drow_merc_ck.ncs index 1af697d8..03d14f5b 100644 Binary files a/_module/ncs/drow_merc_ck.ncs and b/_module/ncs/drow_merc_ck.ncs differ diff --git a/_module/ncs/drowdm_take1.ncs b/_module/ncs/drowdm_take1.ncs index 4e64fda3..229a10e5 100644 Binary files a/_module/ncs/drowdm_take1.ncs and b/_module/ncs/drowdm_take1.ncs differ diff --git a/_module/ncs/drowdm_take2.ncs b/_module/ncs/drowdm_take2.ncs index 066654b9..bf40d0a1 100644 Binary files a/_module/ncs/drowdm_take2.ncs and b/_module/ncs/drowdm_take2.ncs differ diff --git a/_module/ncs/drowdm_take3.ncs b/_module/ncs/drowdm_take3.ncs index a50e774d..c61aedda 100644 Binary files a/_module/ncs/drowdm_take3.ncs and b/_module/ncs/drowdm_take3.ncs differ diff --git a/_module/ncs/drowking_getvar1.ncs b/_module/ncs/drowking_getvar1.ncs index deb55a27..37343e89 100644 Binary files a/_module/ncs/drowking_getvar1.ncs and b/_module/ncs/drowking_getvar1.ncs differ diff --git a/_module/ncs/drowking_getvar2.ncs b/_module/ncs/drowking_getvar2.ncs index db27ec6e..d4027a38 100644 Binary files a/_module/ncs/drowking_getvar2.ncs and b/_module/ncs/drowking_getvar2.ncs differ diff --git a/_module/ncs/drowp_chkvar1.ncs b/_module/ncs/drowp_chkvar1.ncs index 9232fa93..e74f79e2 100644 Binary files a/_module/ncs/drowp_chkvar1.ncs and b/_module/ncs/drowp_chkvar1.ncs differ diff --git a/_module/ncs/drowpriestgetvr1.ncs b/_module/ncs/drowpriestgetvr1.ncs index be2358df..7a2718bf 100644 Binary files a/_module/ncs/drowpriestgetvr1.ncs and b/_module/ncs/drowpriestgetvr1.ncs differ diff --git a/_module/ncs/drowprinces_give.ncs b/_module/ncs/drowprinces_give.ncs index 288c1dd9..12a8366a 100644 Binary files a/_module/ncs/drowprinces_give.ncs and b/_module/ncs/drowprinces_give.ncs differ diff --git a/_module/ncs/druidshop_check.ncs b/_module/ncs/druidshop_check.ncs index d4cc3612..fb01b258 100644 Binary files a/_module/ncs/druidshop_check.ncs and b/_module/ncs/druidshop_check.ncs differ diff --git a/_module/ncs/drunkpirate_ck.ncs b/_module/ncs/drunkpirate_ck.ncs index ee0ee60e..6df53ddb 100644 Binary files a/_module/ncs/drunkpirate_ck.ncs and b/_module/ncs/drunkpirate_ck.ncs differ diff --git a/_module/ncs/dryad_take.ncs b/_module/ncs/dryad_take.ncs index ba205ae1..5c3bac36 100644 Binary files a/_module/ncs/dryad_take.ncs and b/_module/ncs/dryad_take.ncs differ diff --git a/_module/ncs/dwarf_merc_ck.ncs b/_module/ncs/dwarf_merc_ck.ncs index d1835de8..814c76d1 100644 Binary files a/_module/ncs/dwarf_merc_ck.ncs and b/_module/ncs/dwarf_merc_ck.ncs differ diff --git a/_module/ncs/eagle_npc.ncs b/_module/ncs/eagle_npc.ncs index aee8cbc1..3e4cae22 100644 Binary files a/_module/ncs/eagle_npc.ncs and b/_module/ncs/eagle_npc.ncs differ diff --git a/_module/ncs/eaglenpc2.ncs b/_module/ncs/eaglenpc2.ncs index d5f274d8..1965b34d 100644 Binary files a/_module/ncs/eaglenpc2.ncs and b/_module/ncs/eaglenpc2.ncs differ diff --git a/_module/ncs/earthnoble_spawn.ncs b/_module/ncs/earthnoble_spawn.ncs index 9ab0f469..bd706e96 100644 Binary files a/_module/ncs/earthnoble_spawn.ncs and b/_module/ncs/earthnoble_spawn.ncs differ diff --git a/_module/ncs/eldricreward1.ncs b/_module/ncs/eldricreward1.ncs index af72cc80..caf1939c 100644 Binary files a/_module/ncs/eldricreward1.ncs and b/_module/ncs/eldricreward1.ncs differ diff --git a/_module/ncs/eldricreward2.ncs b/_module/ncs/eldricreward2.ncs index 4eae7d3d..7c8f6549 100644 Binary files a/_module/ncs/eldricreward2.ncs and b/_module/ncs/eldricreward2.ncs differ diff --git a/_module/ncs/element_ck_gems1.ncs b/_module/ncs/element_ck_gems1.ncs index 7ebe79ef..36bd6200 100644 Binary files a/_module/ncs/element_ck_gems1.ncs and b/_module/ncs/element_ck_gems1.ncs differ diff --git a/_module/ncs/element_ck_gems2.ncs b/_module/ncs/element_ck_gems2.ncs index 761cc1df..349ec7e5 100644 Binary files a/_module/ncs/element_ck_gems2.ncs and b/_module/ncs/element_ck_gems2.ncs differ diff --git a/_module/ncs/element_ck_var1.ncs b/_module/ncs/element_ck_var1.ncs index 2b82cc38..b3c3ce8b 100644 Binary files a/_module/ncs/element_ck_var1.ncs and b/_module/ncs/element_ck_var1.ncs differ diff --git a/_module/ncs/element_int_ck.ncs b/_module/ncs/element_int_ck.ncs index a5067c82..232090fb 100644 Binary files a/_module/ncs/element_int_ck.ncs and b/_module/ncs/element_int_ck.ncs differ diff --git a/_module/ncs/element_persuade.ncs b/_module/ncs/element_persuade.ncs index 30ba7d71..28d96000 100644 Binary files a/_module/ncs/element_persuade.ncs and b/_module/ncs/element_persuade.ncs differ diff --git a/_module/ncs/element_port2.ncs b/_module/ncs/element_port2.ncs index 28ce0bd5..2db548af 100644 Binary files a/_module/ncs/element_port2.ncs and b/_module/ncs/element_port2.ncs differ diff --git a/_module/ncs/element_takegem1.ncs b/_module/ncs/element_takegem1.ncs index adce5149..c5fa3e13 100644 Binary files a/_module/ncs/element_takegem1.ncs and b/_module/ncs/element_takegem1.ncs differ diff --git a/_module/ncs/emote_rod_give.ncs b/_module/ncs/emote_rod_give.ncs index 3dcceb41..f6613b5c 100644 Binary files a/_module/ncs/emote_rod_give.ncs and b/_module/ncs/emote_rod_give.ncs differ diff --git a/_module/ncs/emotewand.ncs b/_module/ncs/emotewand.ncs index 89403807..98b5a494 100644 Binary files a/_module/ncs/emotewand.ncs and b/_module/ncs/emotewand.ncs differ diff --git a/_module/ncs/emperor_attack.ncs b/_module/ncs/emperor_attack.ncs index 4f64ff1d..a2a3da9b 100644 Binary files a/_module/ncs/emperor_attack.ncs and b/_module/ncs/emperor_attack.ncs differ diff --git a/_module/ncs/end_baltar.ncs b/_module/ncs/end_baltar.ncs index b0b5cf77..893f8353 100644 Binary files a/_module/ncs/end_baltar.ncs and b/_module/ncs/end_baltar.ncs differ diff --git a/_module/ncs/end_band_head.ncs b/_module/ncs/end_band_head.ncs index 9ec60295..d4326002 100644 Binary files a/_module/ncs/end_band_head.ncs and b/_module/ncs/end_band_head.ncs differ diff --git a/_module/ncs/end_cugel_quest.ncs b/_module/ncs/end_cugel_quest.ncs index 001bf1c5..575bba5f 100644 Binary files a/_module/ncs/end_cugel_quest.ncs and b/_module/ncs/end_cugel_quest.ncs differ diff --git a/_module/ncs/end_diary.ncs b/_module/ncs/end_diary.ncs index 92b1b133..814f28ab 100644 Binary files a/_module/ncs/end_diary.ncs and b/_module/ncs/end_diary.ncs differ diff --git a/_module/ncs/end_goblin_head.ncs b/_module/ncs/end_goblin_head.ncs index cca751fe..23f8ef27 100644 Binary files a/_module/ncs/end_goblin_head.ncs and b/_module/ncs/end_goblin_head.ncs differ diff --git a/_module/ncs/end_green.ncs b/_module/ncs/end_green.ncs index 5cd03a58..325fedde 100644 Binary files a/_module/ncs/end_green.ncs and b/_module/ncs/end_green.ncs differ diff --git a/_module/ncs/end_hamley_gear.ncs b/_module/ncs/end_hamley_gear.ncs index 38ac560c..a1038562 100644 Binary files a/_module/ncs/end_hamley_gear.ncs and b/_module/ncs/end_hamley_gear.ncs differ diff --git a/_module/ncs/end_hamleynote.ncs b/_module/ncs/end_hamleynote.ncs index ab2c119b..86dc832d 100644 Binary files a/_module/ncs/end_hamleynote.ncs and b/_module/ncs/end_hamleynote.ncs differ diff --git a/_module/ncs/end_hetmanquest.ncs b/_module/ncs/end_hetmanquest.ncs index 411fd8fa..abaf2860 100644 Binary files a/_module/ncs/end_hetmanquest.ncs and b/_module/ncs/end_hetmanquest.ncs differ diff --git a/_module/ncs/end_irda_stone.ncs b/_module/ncs/end_irda_stone.ncs index c14be563..7aab5268 100644 Binary files a/_module/ncs/end_irda_stone.ncs and b/_module/ncs/end_irda_stone.ncs differ diff --git a/_module/ncs/end_jail_quest.ncs b/_module/ncs/end_jail_quest.ncs index 682dde88..8385f823 100644 Binary files a/_module/ncs/end_jail_quest.ncs and b/_module/ncs/end_jail_quest.ncs differ diff --git a/_module/ncs/end_miller_baby.ncs b/_module/ncs/end_miller_baby.ncs index d65184b5..5dc9466d 100644 Binary files a/_module/ncs/end_miller_baby.ncs and b/_module/ncs/end_miller_baby.ncs differ diff --git a/_module/ncs/end_shaolin.ncs b/_module/ncs/end_shaolin.ncs index 1e061334..a64ba55c 100644 Binary files a/_module/ncs/end_shaolin.ncs and b/_module/ncs/end_shaolin.ncs differ diff --git a/_module/ncs/enter_message1.ncs b/_module/ncs/enter_message1.ncs index ebad4138..5606dc63 100644 Binary files a/_module/ncs/enter_message1.ncs and b/_module/ncs/enter_message1.ncs differ diff --git a/_module/ncs/enter_message10.ncs b/_module/ncs/enter_message10.ncs index 01a429d0..e2020723 100644 Binary files a/_module/ncs/enter_message10.ncs and b/_module/ncs/enter_message10.ncs differ diff --git a/_module/ncs/enter_message11.ncs b/_module/ncs/enter_message11.ncs index 6dae3b8d..de34ba5a 100644 Binary files a/_module/ncs/enter_message11.ncs and b/_module/ncs/enter_message11.ncs differ diff --git a/_module/ncs/enter_message12.ncs b/_module/ncs/enter_message12.ncs index 0e8b70e8..0d80e303 100644 Binary files a/_module/ncs/enter_message12.ncs and b/_module/ncs/enter_message12.ncs differ diff --git a/_module/ncs/enter_message13.ncs b/_module/ncs/enter_message13.ncs index c704e915..7fc11d94 100644 Binary files a/_module/ncs/enter_message13.ncs and b/_module/ncs/enter_message13.ncs differ diff --git a/_module/ncs/enter_message2.ncs b/_module/ncs/enter_message2.ncs index b2da9dae..018289a9 100644 Binary files a/_module/ncs/enter_message2.ncs and b/_module/ncs/enter_message2.ncs differ diff --git a/_module/ncs/enter_message3.ncs b/_module/ncs/enter_message3.ncs index 8ed4078c..0243f36d 100644 Binary files a/_module/ncs/enter_message3.ncs and b/_module/ncs/enter_message3.ncs differ diff --git a/_module/ncs/enter_message4.ncs b/_module/ncs/enter_message4.ncs index e00962c9..4b734272 100644 Binary files a/_module/ncs/enter_message4.ncs and b/_module/ncs/enter_message4.ncs differ diff --git a/_module/ncs/enter_message5.ncs b/_module/ncs/enter_message5.ncs index 699a18e0..2aa1e4ef 100644 Binary files a/_module/ncs/enter_message5.ncs and b/_module/ncs/enter_message5.ncs differ diff --git a/_module/ncs/enter_message6.ncs b/_module/ncs/enter_message6.ncs index 3eeb1bb9..7ddafd5b 100644 Binary files a/_module/ncs/enter_message6.ncs and b/_module/ncs/enter_message6.ncs differ diff --git a/_module/ncs/enter_message7.ncs b/_module/ncs/enter_message7.ncs index 0a9e7149..a5098318 100644 Binary files a/_module/ncs/enter_message7.ncs and b/_module/ncs/enter_message7.ncs differ diff --git a/_module/ncs/enter_message8.ncs b/_module/ncs/enter_message8.ncs index 4e23328f..2aa6e37d 100644 Binary files a/_module/ncs/enter_message8.ncs and b/_module/ncs/enter_message8.ncs differ diff --git a/_module/ncs/enter_message9.ncs b/_module/ncs/enter_message9.ncs index 0a1ffbc7..b5799368 100644 Binary files a/_module/ncs/enter_message9.ncs and b/_module/ncs/enter_message9.ncs differ diff --git a/_module/ncs/enter_messagerh1.ncs b/_module/ncs/enter_messagerh1.ncs index bff45b46..10077430 100644 Binary files a/_module/ncs/enter_messagerh1.ncs and b/_module/ncs/enter_messagerh1.ncs differ diff --git a/_module/ncs/enter_messagerh2.ncs b/_module/ncs/enter_messagerh2.ncs index 8136130d..1cfcf0d5 100644 Binary files a/_module/ncs/enter_messagerh2.ncs and b/_module/ncs/enter_messagerh2.ncs differ diff --git a/_module/ncs/enter_messagerh3.ncs b/_module/ncs/enter_messagerh3.ncs index 83e96e83..d4219fcf 100644 Binary files a/_module/ncs/enter_messagerh3.ncs and b/_module/ncs/enter_messagerh3.ncs differ diff --git a/_module/ncs/escort_death.ncs b/_module/ncs/escort_death.ncs index dfed0200..043c4c2b 100644 Binary files a/_module/ncs/escort_death.ncs and b/_module/ncs/escort_death.ncs differ diff --git a/_module/ncs/escort_follow5.ncs b/_module/ncs/escort_follow5.ncs index 49910cdf..7ba4a320 100644 Binary files a/_module/ncs/escort_follow5.ncs and b/_module/ncs/escort_follow5.ncs differ diff --git a/_module/ncs/escort_follow6.ncs b/_module/ncs/escort_follow6.ncs index 8fd88e18..bdd46c7a 100644 Binary files a/_module/ncs/escort_follow6.ncs and b/_module/ncs/escort_follow6.ncs differ diff --git a/_module/ncs/escort_getvar1.ncs b/_module/ncs/escort_getvar1.ncs index ed0fc490..785c6196 100644 Binary files a/_module/ncs/escort_getvar1.ncs and b/_module/ncs/escort_getvar1.ncs differ diff --git a/_module/ncs/escort_getvar2.ncs b/_module/ncs/escort_getvar2.ncs index a5ca84de..0d2a77a0 100644 Binary files a/_module/ncs/escort_getvar2.ncs and b/_module/ncs/escort_getvar2.ncs differ diff --git a/_module/ncs/escort_getvar3.ncs b/_module/ncs/escort_getvar3.ncs index aea0ec0d..2b28282c 100644 Binary files a/_module/ncs/escort_getvar3.ncs and b/_module/ncs/escort_getvar3.ncs differ diff --git a/_module/ncs/escort_getvar4.ncs b/_module/ncs/escort_getvar4.ncs index 38cd39dc..ceefd42f 100644 Binary files a/_module/ncs/escort_getvar4.ncs and b/_module/ncs/escort_getvar4.ncs differ diff --git a/_module/ncs/escort_getvar5.ncs b/_module/ncs/escort_getvar5.ncs index fa3e8a99..5666ca40 100644 Binary files a/_module/ncs/escort_getvar5.ncs and b/_module/ncs/escort_getvar5.ncs differ diff --git a/_module/ncs/escort_getvar6.ncs b/_module/ncs/escort_getvar6.ncs index e212d678..ea20e4ee 100644 Binary files a/_module/ncs/escort_getvar6.ncs and b/_module/ncs/escort_getvar6.ncs differ diff --git a/_module/ncs/escort_getvar7.ncs b/_module/ncs/escort_getvar7.ncs index a5303337..7b407f34 100644 Binary files a/_module/ncs/escort_getvar7.ncs and b/_module/ncs/escort_getvar7.ncs differ diff --git a/_module/ncs/escort_getvar8.ncs b/_module/ncs/escort_getvar8.ncs index 5406e33a..9a719dcf 100644 Binary files a/_module/ncs/escort_getvar8.ncs and b/_module/ncs/escort_getvar8.ncs differ diff --git a/_module/ncs/escort_home.ncs b/_module/ncs/escort_home.ncs index 831c3eef..b0c2bed8 100644 Binary files a/_module/ncs/escort_home.ncs and b/_module/ncs/escort_home.ncs differ diff --git a/_module/ncs/escort_reset2.ncs b/_module/ncs/escort_reset2.ncs index 641ed400..ade08356 100644 Binary files a/_module/ncs/escort_reset2.ncs and b/_module/ncs/escort_reset2.ncs differ diff --git a/_module/ncs/escort_spawn.ncs b/_module/ncs/escort_spawn.ncs index b98dea9e..695dd197 100644 Binary files a/_module/ncs/escort_spawn.ncs and b/_module/ncs/escort_spawn.ncs differ diff --git a/_module/ncs/escort_stop.ncs b/_module/ncs/escort_stop.ncs index ebc95241..3989c7b5 100644 Binary files a/_module/ncs/escort_stop.ncs and b/_module/ncs/escort_stop.ncs differ diff --git a/_module/ncs/escort_userdef.ncs b/_module/ncs/escort_userdef.ncs index 9dca356e..96e26a70 100644 Binary files a/_module/ncs/escort_userdef.ncs and b/_module/ncs/escort_userdef.ncs differ diff --git a/_module/ncs/explode_on_death.ncs b/_module/ncs/explode_on_death.ncs index c3371fb6..28a4de7c 100644 Binary files a/_module/ncs/explode_on_death.ncs and b/_module/ncs/explode_on_death.ncs differ diff --git a/_module/ncs/explosive_death.ncs b/_module/ncs/explosive_death.ncs index 310d5a28..e9f65ca7 100644 Binary files a/_module/ncs/explosive_death.ncs and b/_module/ncs/explosive_death.ncs differ diff --git a/_module/ncs/fairy_givetake.ncs b/_module/ncs/fairy_givetake.ncs index f77bd2a4..4aef6d4f 100644 Binary files a/_module/ncs/fairy_givetake.ncs and b/_module/ncs/fairy_givetake.ncs differ diff --git a/_module/ncs/fairygetvar1.ncs b/_module/ncs/fairygetvar1.ncs index 265b48cc..ff1cf974 100644 Binary files a/_module/ncs/fairygetvar1.ncs and b/_module/ncs/fairygetvar1.ncs differ diff --git a/_module/ncs/farmhand.ncs b/_module/ncs/farmhand.ncs index c6e52ad4..4f4209f0 100644 Binary files a/_module/ncs/farmhand.ncs and b/_module/ncs/farmhand.ncs differ diff --git a/_module/ncs/farmhand2.ncs b/_module/ncs/farmhand2.ncs index 7ad91775..fb16e2bf 100644 Binary files a/_module/ncs/farmhand2.ncs and b/_module/ncs/farmhand2.ncs differ diff --git a/_module/ncs/farmhand3.ncs b/_module/ncs/farmhand3.ncs index b85f5894..d6de03c4 100644 Binary files a/_module/ncs/farmhand3.ncs and b/_module/ncs/farmhand3.ncs differ diff --git a/_module/ncs/farmhand4.ncs b/_module/ncs/farmhand4.ncs index 3728226a..56e93e03 100644 Binary files a/_module/ncs/farmhand4.ncs and b/_module/ncs/farmhand4.ncs differ diff --git a/_module/ncs/farmhand5.ncs b/_module/ncs/farmhand5.ncs index d343f840..ae3b6202 100644 Binary files a/_module/ncs/farmhand5.ncs and b/_module/ncs/farmhand5.ncs differ diff --git a/_module/ncs/fb1_onenter.ncs b/_module/ncs/fb1_onenter.ncs index c822298a..fbec03db 100644 Binary files a/_module/ncs/fb1_onenter.ncs and b/_module/ncs/fb1_onenter.ncs differ diff --git a/_module/ncs/fho_glow.ncs b/_module/ncs/fho_glow.ncs index 3431e02d..2bb3b808 100644 Binary files a/_module/ncs/fho_glow.ncs and b/_module/ncs/fho_glow.ncs differ diff --git a/_module/ncs/fiend_death.ncs b/_module/ncs/fiend_death.ncs index 9de77136..9643e7c9 100644 Binary files a/_module/ncs/fiend_death.ncs and b/_module/ncs/fiend_death.ncs differ diff --git a/_module/ncs/findfriend.ncs b/_module/ncs/findfriend.ncs index 2382fdab..6fdbd31c 100644 Binary files a/_module/ncs/findfriend.ncs and b/_module/ncs/findfriend.ncs differ diff --git a/_module/ncs/firelord_spawn.ncs b/_module/ncs/firelord_spawn.ncs index 7547e439..4a3d96e9 100644 Binary files a/_module/ncs/firelord_spawn.ncs and b/_module/ncs/firelord_spawn.ncs differ diff --git a/_module/ncs/flying_boots.ncs b/_module/ncs/flying_boots.ncs index d1c06792..f01296cf 100644 Binary files a/_module/ncs/flying_boots.ncs and b/_module/ncs/flying_boots.ncs differ diff --git a/_module/ncs/forcefield_wolf1.ncs b/_module/ncs/forcefield_wolf1.ncs index 80cdfb1b..b0d23a62 100644 Binary files a/_module/ncs/forcefield_wolf1.ncs and b/_module/ncs/forcefield_wolf1.ncs differ diff --git a/_module/ncs/forgemoneycheck.ncs b/_module/ncs/forgemoneycheck.ncs index d15ac724..d06328a6 100644 Binary files a/_module/ncs/forgemoneycheck.ncs and b/_module/ncs/forgemoneycheck.ncs differ diff --git a/_module/ncs/forgemoneylgweap.ncs b/_module/ncs/forgemoneylgweap.ncs index 64d76ee6..ff820157 100644 Binary files a/_module/ncs/forgemoneylgweap.ncs and b/_module/ncs/forgemoneylgweap.ncs differ diff --git a/_module/ncs/frobo_flee.ncs b/_module/ncs/frobo_flee.ncs index a47b65a5..1782f819 100644 Binary files a/_module/ncs/frobo_flee.ncs and b/_module/ncs/frobo_flee.ncs differ diff --git a/_module/ncs/frobo_getvar1.ncs b/_module/ncs/frobo_getvar1.ncs index 8e174848..8eaab3f2 100644 Binary files a/_module/ncs/frobo_getvar1.ncs and b/_module/ncs/frobo_getvar1.ncs differ diff --git a/_module/ncs/frobo_spawn.ncs b/_module/ncs/frobo_spawn.ncs index 987788fd..56ddd87e 100644 Binary files a/_module/ncs/frobo_spawn.ncs and b/_module/ncs/frobo_spawn.ncs differ diff --git a/_module/ncs/frobo_take_stat.ncs b/_module/ncs/frobo_take_stat.ncs index 19824b6c..ddd75778 100644 Binary files a/_module/ncs/frobo_take_stat.ncs and b/_module/ncs/frobo_take_stat.ncs differ diff --git a/_module/ncs/frosty_get1.ncs b/_module/ncs/frosty_get1.ncs index 7952ee46..96e900c5 100644 Binary files a/_module/ncs/frosty_get1.ncs and b/_module/ncs/frosty_get1.ncs differ diff --git a/_module/ncs/frosty_get2.ncs b/_module/ncs/frosty_get2.ncs index ee594be3..c986c601 100644 Binary files a/_module/ncs/frosty_get2.ncs and b/_module/ncs/frosty_get2.ncs differ diff --git a/_module/ncs/fx_hbdiv.ncs b/_module/ncs/fx_hbdiv.ncs index 9fa7ea52..116dd661 100644 Binary files a/_module/ncs/fx_hbdiv.ncs and b/_module/ncs/fx_hbdiv.ncs differ diff --git a/_module/ncs/g_alrdy_spoken.ncs b/_module/ncs/g_alrdy_spoken.ncs index e617fd08..9ecd98a7 100644 Binary files a/_module/ncs/g_alrdy_spoken.ncs and b/_module/ncs/g_alrdy_spoken.ncs differ diff --git a/_module/ncs/g_alrdy_spoken11.ncs b/_module/ncs/g_alrdy_spoken11.ncs index b7d890cc..edb7772f 100644 Binary files a/_module/ncs/g_alrdy_spoken11.ncs and b/_module/ncs/g_alrdy_spoken11.ncs differ diff --git a/_module/ncs/g_alrdy_spoken2.ncs b/_module/ncs/g_alrdy_spoken2.ncs index 1b4ec3cb..1101ffe8 100644 Binary files a/_module/ncs/g_alrdy_spoken2.ncs and b/_module/ncs/g_alrdy_spoken2.ncs differ diff --git a/_module/ncs/g_alrdy_spoken6.ncs b/_module/ncs/g_alrdy_spoken6.ncs index 8c687c0a..9f755347 100644 Binary files a/_module/ncs/g_alrdy_spoken6.ncs and b/_module/ncs/g_alrdy_spoken6.ncs differ diff --git a/_module/ncs/g_alrdy_spoken7.ncs b/_module/ncs/g_alrdy_spoken7.ncs index a312c194..03ce453d 100644 Binary files a/_module/ncs/g_alrdy_spoken7.ncs and b/_module/ncs/g_alrdy_spoken7.ncs differ diff --git a/_module/ncs/g_alrdy_spoken8.ncs b/_module/ncs/g_alrdy_spoken8.ncs index 0234bda1..8018e9d5 100644 Binary files a/_module/ncs/g_alrdy_spoken8.ncs and b/_module/ncs/g_alrdy_spoken8.ncs differ diff --git a/_module/ncs/g_alrdy_spoken9.ncs b/_module/ncs/g_alrdy_spoken9.ncs index 84e33135..42f177ce 100644 Binary files a/_module/ncs/g_alrdy_spoken9.ncs and b/_module/ncs/g_alrdy_spoken9.ncs differ diff --git a/_module/ncs/gdragon_ckvar1.ncs b/_module/ncs/gdragon_ckvar1.ncs index cda3cf4e..9ce86d1c 100644 Binary files a/_module/ncs/gdragon_ckvar1.ncs and b/_module/ncs/gdragon_ckvar1.ncs differ diff --git a/_module/ncs/gdragon_done.ncs b/_module/ncs/gdragon_done.ncs index 46455686..9c8ad1a5 100644 Binary files a/_module/ncs/gdragon_done.ncs and b/_module/ncs/gdragon_done.ncs differ diff --git a/_module/ncs/gen_treasure1.ncs b/_module/ncs/gen_treasure1.ncs index 3f47a296..7fb0512e 100644 Binary files a/_module/ncs/gen_treasure1.ncs and b/_module/ncs/gen_treasure1.ncs differ diff --git a/_module/ncs/get_nurgle_key.ncs b/_module/ncs/get_nurgle_key.ncs index a62246ee..4d5612da 100644 Binary files a/_module/ncs/get_nurgle_key.ncs and b/_module/ncs/get_nurgle_key.ncs differ diff --git a/_module/ncs/ghost_ck_ring.ncs b/_module/ncs/ghost_ck_ring.ncs index 7f62ba20..4f7f5752 100644 Binary files a/_module/ncs/ghost_ck_ring.ncs and b/_module/ncs/ghost_ck_ring.ncs differ diff --git a/_module/ncs/ghost_spawn.ncs b/_module/ncs/ghost_spawn.ncs index 3ecfa282..7f0d98af 100644 Binary files a/_module/ncs/ghost_spawn.ncs and b/_module/ncs/ghost_spawn.ncs differ diff --git a/_module/ncs/ghostvfx.ncs b/_module/ncs/ghostvfx.ncs index 00731a32..7e751e1f 100644 Binary files a/_module/ncs/ghostvfx.ncs and b/_module/ncs/ghostvfx.ncs differ diff --git a/_module/ncs/giles_done2.ncs b/_module/ncs/giles_done2.ncs index 2eb0685b..54dd2f2a 100644 Binary files a/_module/ncs/giles_done2.ncs and b/_module/ncs/giles_done2.ncs differ diff --git a/_module/ncs/ginge_talktime11.ncs b/_module/ncs/ginge_talktime11.ncs index 4921abe5..d7d509e8 100644 Binary files a/_module/ncs/ginge_talktime11.ncs and b/_module/ncs/ginge_talktime11.ncs differ diff --git a/_module/ncs/ginge_talktimes.ncs b/_module/ncs/ginge_talktimes.ncs index 359500ad..68e5afe9 100644 Binary files a/_module/ncs/ginge_talktimes.ncs and b/_module/ncs/ginge_talktimes.ncs differ diff --git a/_module/ncs/ginge_talktimes2.ncs b/_module/ncs/ginge_talktimes2.ncs index 6e838f33..08ae8da4 100644 Binary files a/_module/ncs/ginge_talktimes2.ncs and b/_module/ncs/ginge_talktimes2.ncs differ diff --git a/_module/ncs/ginge_talktimes4.ncs b/_module/ncs/ginge_talktimes4.ncs index ead525aa..7723472c 100644 Binary files a/_module/ncs/ginge_talktimes4.ncs and b/_module/ncs/ginge_talktimes4.ncs differ diff --git a/_module/ncs/ginge_talktimes6.ncs b/_module/ncs/ginge_talktimes6.ncs index 8d18af51..28cb54ea 100644 Binary files a/_module/ncs/ginge_talktimes6.ncs and b/_module/ncs/ginge_talktimes6.ncs differ diff --git a/_module/ncs/ginge_talktimes7.ncs b/_module/ncs/ginge_talktimes7.ncs index 20eae7f6..1f87e969 100644 Binary files a/_module/ncs/ginge_talktimes7.ncs and b/_module/ncs/ginge_talktimes7.ncs differ diff --git a/_module/ncs/ginge_talktimes8.ncs b/_module/ncs/ginge_talktimes8.ncs index 5994dce8..ddb15396 100644 Binary files a/_module/ncs/ginge_talktimes8.ncs and b/_module/ncs/ginge_talktimes8.ncs differ diff --git a/_module/ncs/ginge_talktimes9.ncs b/_module/ncs/ginge_talktimes9.ncs index 70788ec6..27028d6f 100644 Binary files a/_module/ncs/ginge_talktimes9.ncs and b/_module/ncs/ginge_talktimes9.ncs differ diff --git a/_module/ncs/give_150xp.ncs b/_module/ncs/give_150xp.ncs index ff0680a1..9ed033ae 100644 Binary files a/_module/ncs/give_150xp.ncs and b/_module/ncs/give_150xp.ncs differ diff --git a/_module/ncs/give_150xp2.ncs b/_module/ncs/give_150xp2.ncs index a4c5c700..30d494ac 100644 Binary files a/_module/ncs/give_150xp2.ncs and b/_module/ncs/give_150xp2.ncs differ diff --git a/_module/ncs/give_150xp3.ncs b/_module/ncs/give_150xp3.ncs index 21a63c41..538cc4f4 100644 Binary files a/_module/ncs/give_150xp3.ncs and b/_module/ncs/give_150xp3.ncs differ diff --git a/_module/ncs/give_dragonorb.ncs b/_module/ncs/give_dragonorb.ncs index 2f100f3a..6ea715cf 100644 Binary files a/_module/ncs/give_dragonorb.ncs and b/_module/ncs/give_dragonorb.ncs differ diff --git a/_module/ncs/give_dryad_item.ncs b/_module/ncs/give_dryad_item.ncs index 32a5cd82..f6787c72 100644 Binary files a/_module/ncs/give_dryad_item.ncs and b/_module/ncs/give_dryad_item.ncs differ diff --git a/_module/ncs/gold_spawn.ncs b/_module/ncs/gold_spawn.ncs index ff55c2fd..865133b4 100644 Binary files a/_module/ncs/gold_spawn.ncs and b/_module/ncs/gold_spawn.ncs differ diff --git a/_module/ncs/golemdespawn.ncs b/_module/ncs/golemdespawn.ncs index 390bdd97..e33f817c 100644 Binary files a/_module/ncs/golemdespawn.ncs and b/_module/ncs/golemdespawn.ncs differ diff --git a/_module/ncs/golemgold.ncs b/_module/ncs/golemgold.ncs index a6e7ce9c..e434e5d7 100644 Binary files a/_module/ncs/golemgold.ncs and b/_module/ncs/golemgold.ncs differ diff --git a/_module/ncs/golemgoldtoken.ncs b/_module/ncs/golemgoldtoken.ncs index 3f716644..645eb7ff 100644 Binary files a/_module/ncs/golemgoldtoken.ncs and b/_module/ncs/golemgoldtoken.ncs differ diff --git a/_module/ncs/golemnogold.ncs b/_module/ncs/golemnogold.ncs index f26d3a05..8f6dc367 100644 Binary files a/_module/ncs/golemnogold.ncs and b/_module/ncs/golemnogold.ncs differ diff --git a/_module/ncs/golemrentfee.ncs b/_module/ncs/golemrentfee.ncs index a5fd7d14..dc03b007 100644 Binary files a/_module/ncs/golemrentfee.ncs and b/_module/ncs/golemrentfee.ncs differ diff --git a/_module/ncs/green_ck_cmpgn.ncs b/_module/ncs/green_ck_cmpgn.ncs index fe4bc96b..8b6a79f0 100644 Binary files a/_module/ncs/green_ck_cmpgn.ncs and b/_module/ncs/green_ck_cmpgn.ncs differ diff --git a/_module/ncs/green_done.ncs b/_module/ncs/green_done.ncs index d5052337..d4e13286 100644 Binary files a/_module/ncs/green_done.ncs and b/_module/ncs/green_done.ncs differ diff --git a/_module/ncs/green_setvar.ncs b/_module/ncs/green_setvar.ncs index 6e5c3219..09b0ac2b 100644 Binary files a/_module/ncs/green_setvar.ncs and b/_module/ncs/green_setvar.ncs differ diff --git a/_module/ncs/green_spoken2.ncs b/_module/ncs/green_spoken2.ncs index 55e7cb49..a61e595c 100644 Binary files a/_module/ncs/green_spoken2.ncs and b/_module/ncs/green_spoken2.ncs differ diff --git a/_module/ncs/gsokeygen.ncs b/_module/ncs/gsokeygen.ncs index 33775d39..cd071f4d 100644 Binary files a/_module/ncs/gsokeygen.ncs and b/_module/ncs/gsokeygen.ncs differ diff --git a/_module/ncs/gz_healer_spawn.ncs b/_module/ncs/gz_healer_spawn.ncs index 549c75bb..8d0aec15 100644 Binary files a/_module/ncs/gz_healer_spawn.ncs and b/_module/ncs/gz_healer_spawn.ncs differ diff --git a/_module/ncs/gz_healer_ude.ncs b/_module/ncs/gz_healer_ude.ncs index f94300b3..44d404d0 100644 Binary files a/_module/ncs/gz_healer_ude.ncs and b/_module/ncs/gz_healer_ude.ncs differ diff --git a/_module/ncs/gzcc_healertoken.ncs b/_module/ncs/gzcc_healertoken.ncs index af11666b..0503a650 100644 Binary files a/_module/ncs/gzcc_healertoken.ncs and b/_module/ncs/gzcc_healertoken.ncs differ diff --git a/_module/ncs/gzcc_is_pc_abdmg.ncs b/_module/ncs/gzcc_is_pc_abdmg.ncs index 0683f1df..5a614073 100644 Binary files a/_module/ncs/gzcc_is_pc_abdmg.ncs and b/_module/ncs/gzcc_is_pc_abdmg.ncs differ diff --git a/_module/ncs/gzcc_is_pc_blind.ncs b/_module/ncs/gzcc_is_pc_blind.ncs index 6340b373..af7fcccc 100644 Binary files a/_module/ncs/gzcc_is_pc_blind.ncs and b/_module/ncs/gzcc_is_pc_blind.ncs differ diff --git a/_module/ncs/gzcc_is_pc_curse.ncs b/_module/ncs/gzcc_is_pc_curse.ncs index b5675750..a96f7874 100644 Binary files a/_module/ncs/gzcc_is_pc_curse.ncs and b/_module/ncs/gzcc_is_pc_curse.ncs differ diff --git a/_module/ncs/gzcc_is_pc_disea.ncs b/_module/ncs/gzcc_is_pc_disea.ncs index 10103ec7..95f83468 100644 Binary files a/_module/ncs/gzcc_is_pc_disea.ncs and b/_module/ncs/gzcc_is_pc_disea.ncs differ diff --git a/_module/ncs/gzcc_is_pc_dmged.ncs b/_module/ncs/gzcc_is_pc_dmged.ncs index f4d83a07..75e85f59 100644 Binary files a/_module/ncs/gzcc_is_pc_dmged.ncs and b/_module/ncs/gzcc_is_pc_dmged.ncs differ diff --git a/_module/ncs/gzcc_is_pc_neglv.ncs b/_module/ncs/gzcc_is_pc_neglv.ncs index 5b67322d..eb93fb61 100644 Binary files a/_module/ncs/gzcc_is_pc_neglv.ncs and b/_module/ncs/gzcc_is_pc_neglv.ncs differ diff --git a/_module/ncs/gzcc_is_pc_poiso.ncs b/_module/ncs/gzcc_is_pc_poiso.ncs index 28bff16c..adc419c0 100644 Binary files a/_module/ncs/gzcc_is_pc_poiso.ncs and b/_module/ncs/gzcc_is_pc_poiso.ncs differ diff --git a/_module/ncs/gzcc_needhealer.ncs b/_module/ncs/gzcc_needhealer.ncs index 0adcf8e1..2b28fbc1 100644 Binary files a/_module/ncs/gzcc_needhealer.ncs and b/_module/ncs/gzcc_needhealer.ncs differ diff --git a/_module/ncs/gzcs_heal_damage.ncs b/_module/ncs/gzcs_heal_damage.ncs index f55f21b8..2aef4b8e 100644 Binary files a/_module/ncs/gzcs_heal_damage.ncs and b/_module/ncs/gzcs_heal_damage.ncs differ diff --git a/_module/ncs/gzcs_rem_abdmg.ncs b/_module/ncs/gzcs_rem_abdmg.ncs index bae9e246..af0350ef 100644 Binary files a/_module/ncs/gzcs_rem_abdmg.ncs and b/_module/ncs/gzcs_rem_abdmg.ncs differ diff --git a/_module/ncs/gzcs_rem_all.ncs b/_module/ncs/gzcs_rem_all.ncs index 0b477436..30727d7a 100644 Binary files a/_module/ncs/gzcs_rem_all.ncs and b/_module/ncs/gzcs_rem_all.ncs differ diff --git a/_module/ncs/gzcs_rem_blind.ncs b/_module/ncs/gzcs_rem_blind.ncs index 47f549c4..146c1f3c 100644 Binary files a/_module/ncs/gzcs_rem_blind.ncs and b/_module/ncs/gzcs_rem_blind.ncs differ diff --git a/_module/ncs/gzcs_rem_curse.ncs b/_module/ncs/gzcs_rem_curse.ncs index 4cf8a045..97daed7e 100644 Binary files a/_module/ncs/gzcs_rem_curse.ncs and b/_module/ncs/gzcs_rem_curse.ncs differ diff --git a/_module/ncs/gzcs_rem_disease.ncs b/_module/ncs/gzcs_rem_disease.ncs index 80206604..2dfc71ac 100644 Binary files a/_module/ncs/gzcs_rem_disease.ncs and b/_module/ncs/gzcs_rem_disease.ncs differ diff --git a/_module/ncs/gzcs_rem_neglvls.ncs b/_module/ncs/gzcs_rem_neglvls.ncs index de550dd0..308cb5b3 100644 Binary files a/_module/ncs/gzcs_rem_neglvls.ncs and b/_module/ncs/gzcs_rem_neglvls.ncs differ diff --git a/_module/ncs/gzcs_rem_poison.ncs b/_module/ncs/gzcs_rem_poison.ncs index 6486fec8..6509bd3c 100644 Binary files a/_module/ncs/gzcs_rem_poison.ncs and b/_module/ncs/gzcs_rem_poison.ncs differ diff --git a/_module/ncs/hamle_royalseal.ncs b/_module/ncs/hamle_royalseal.ncs index 553c94df..ddb11361 100644 Binary files a/_module/ncs/hamle_royalseal.ncs and b/_module/ncs/hamle_royalseal.ncs differ diff --git a/_module/ncs/hamley_done.ncs b/_module/ncs/hamley_done.ncs index a5389c10..61980341 100644 Binary files a/_module/ncs/hamley_done.ncs and b/_module/ncs/hamley_done.ncs differ diff --git a/_module/ncs/hamley_spoken.ncs b/_module/ncs/hamley_spoken.ncs index bd8982d0..965cccbd 100644 Binary files a/_module/ncs/hamley_spoken.ncs and b/_module/ncs/hamley_spoken.ncs differ diff --git a/_module/ncs/hamley_spoken2.ncs b/_module/ncs/hamley_spoken2.ncs index a8374641..b871a6ae 100644 Binary files a/_module/ncs/hamley_spoken2.ncs and b/_module/ncs/hamley_spoken2.ncs differ diff --git a/_module/ncs/hasflameweap.ncs b/_module/ncs/hasflameweap.ncs index 38a2247c..2ceadc0b 100644 Binary files a/_module/ncs/hasflameweap.ncs and b/_module/ncs/hasflameweap.ncs differ diff --git a/_module/ncs/hasmassheal.ncs b/_module/ncs/hasmassheal.ncs index 64208ffd..04e56993 100644 Binary files a/_module/ncs/hasmassheal.ncs and b/_module/ncs/hasmassheal.ncs differ diff --git a/_module/ncs/hastenser.ncs b/_module/ncs/hastenser.ncs index 22c04332..07a5514c 100644 Binary files a/_module/ncs/hastenser.ncs and b/_module/ncs/hastenser.ncs differ diff --git a/_module/ncs/havendoor_lock.ncs b/_module/ncs/havendoor_lock.ncs index 1550491d..0a9dd889 100644 Binary files a/_module/ncs/havendoor_lock.ncs and b/_module/ncs/havendoor_lock.ncs differ diff --git a/_module/ncs/havendoor_unlock.ncs b/_module/ncs/havendoor_unlock.ncs index d7f3801e..996d7569 100644 Binary files a/_module/ncs/havendoor_unlock.ncs and b/_module/ncs/havendoor_unlock.ncs differ diff --git a/_module/ncs/hb_face_pc.ncs b/_module/ncs/hb_face_pc.ncs index 0d541f36..cc9a9581 100644 Binary files a/_module/ncs/hb_face_pc.ncs and b/_module/ncs/hb_face_pc.ncs differ diff --git a/_module/ncs/hb_light_fx.ncs b/_module/ncs/hb_light_fx.ncs index d07d2f65..58c3d0da 100644 Binary files a/_module/ncs/hb_light_fx.ncs and b/_module/ncs/hb_light_fx.ncs differ diff --git a/_module/ncs/hb_riftport.ncs b/_module/ncs/hb_riftport.ncs index 373500dd..720878e8 100644 Binary files a/_module/ncs/hb_riftport.ncs and b/_module/ncs/hb_riftport.ncs differ diff --git a/_module/ncs/healcheck25.ncs b/_module/ncs/healcheck25.ncs index cc3f7706..24e92c97 100644 Binary files a/_module/ncs/healcheck25.ncs and b/_module/ncs/healcheck25.ncs differ diff --git a/_module/ncs/healcheck50.ncs b/_module/ncs/healcheck50.ncs index f0fd1179..4dd6b5f3 100644 Binary files a/_module/ncs/healcheck50.ncs and b/_module/ncs/healcheck50.ncs differ diff --git a/_module/ncs/healcheck75.ncs b/_module/ncs/healcheck75.ncs index f610d2ad..6f88550a 100644 Binary files a/_module/ncs/healcheck75.ncs and b/_module/ncs/healcheck75.ncs differ diff --git a/_module/ncs/hen_2traps_sc.ncs b/_module/ncs/hen_2traps_sc.ncs index 3d94e23d..21cfc6bd 100644 Binary files a/_module/ncs/hen_2traps_sc.ncs and b/_module/ncs/hen_2traps_sc.ncs differ diff --git a/_module/ncs/hen_3traps_sc.ncs b/_module/ncs/hen_3traps_sc.ncs index 98dd5119..e95cfa03 100644 Binary files a/_module/ncs/hen_3traps_sc.ncs and b/_module/ncs/hen_3traps_sc.ncs differ diff --git a/_module/ncs/hen_4traps_sc.ncs b/_module/ncs/hen_4traps_sc.ncs index 19c983d7..5c9524df 100644 Binary files a/_module/ncs/hen_4traps_sc.ncs and b/_module/ncs/hen_4traps_sc.ncs differ diff --git a/_module/ncs/hen_5traps_sc.ncs b/_module/ncs/hen_5traps_sc.ncs index e7cd8263..16570f1e 100644 Binary files a/_module/ncs/hen_5traps_sc.ncs and b/_module/ncs/hen_5traps_sc.ncs differ diff --git a/_module/ncs/hen_6traps_sc.ncs b/_module/ncs/hen_6traps_sc.ncs index a4e543e7..240392ef 100644 Binary files a/_module/ncs/hen_6traps_sc.ncs and b/_module/ncs/hen_6traps_sc.ncs differ diff --git a/_module/ncs/hen_7traps_sc.ncs b/_module/ncs/hen_7traps_sc.ncs index 11122a71..d3b655f0 100644 Binary files a/_module/ncs/hen_7traps_sc.ncs and b/_module/ncs/hen_7traps_sc.ncs differ diff --git a/_module/ncs/hen_8traps_sc.ncs b/_module/ncs/hen_8traps_sc.ncs index 37058a21..493ba6b1 100644 Binary files a/_module/ncs/hen_8traps_sc.ncs and b/_module/ncs/hen_8traps_sc.ncs differ diff --git a/_module/ncs/hen_canset_sc.ncs b/_module/ncs/hen_canset_sc.ncs index 53913356..c74fe55b 100644 Binary files a/_module/ncs/hen_canset_sc.ncs and b/_module/ncs/hen_canset_sc.ncs differ diff --git a/_module/ncs/hen_gentokens_at.ncs b/_module/ncs/hen_gentokens_at.ncs index 606e7992..5d51211f 100644 Binary files a/_module/ncs/hen_gentokens_at.ncs and b/_module/ncs/hen_gentokens_at.ncs differ diff --git a/_module/ncs/hen_hascompa.ncs b/_module/ncs/hen_hascompa.ncs index 0ce621a0..cd73882b 100644 Binary files a/_module/ncs/hen_hascompa.ncs and b/_module/ncs/hen_hascompa.ncs differ diff --git a/_module/ncs/hen_hasfamil.ncs b/_module/ncs/hen_hasfamil.ncs index a50ebf7c..5124466a 100644 Binary files a/_module/ncs/hen_hasfamil.ncs and b/_module/ncs/hen_hasfamil.ncs differ diff --git a/_module/ncs/hen_settrloc_at.ncs b/_module/ncs/hen_settrloc_at.ncs index 29d47f9c..f9be3239 100644 Binary files a/_module/ncs/hen_settrloc_at.ncs and b/_module/ncs/hen_settrloc_at.ncs differ diff --git a/_module/ncs/hen_trcanset_sc.ncs b/_module/ncs/hen_trcanset_sc.ncs index 22af9643..a424e65c 100644 Binary files a/_module/ncs/hen_trcanset_sc.ncs and b/_module/ncs/hen_trcanset_sc.ncs differ diff --git a/_module/ncs/hench_isautounlo.ncs b/_module/ncs/hench_isautounlo.ncs index 748fe68f..e5338946 100644 Binary files a/_module/ncs/hench_isautounlo.ncs and b/_module/ncs/hench_isautounlo.ncs differ diff --git a/_module/ncs/hench_isopenches.ncs b/_module/ncs/hench_isopenches.ncs index a35a53fa..c878109d 100644 Binary files a/_module/ncs/hench_isopenches.ncs and b/_module/ncs/hench_isopenches.ncs differ diff --git a/_module/ncs/hench_ispickup.ncs b/_module/ncs/hench_ispickup.ncs index 4362bb7f..0f88df0f 100644 Binary files a/_module/ncs/hench_ispickup.ncs and b/_module/ncs/hench_ispickup.ncs differ diff --git a/_module/ncs/hench_isrecover.ncs b/_module/ncs/hench_isrecover.ncs index d44530ac..1641c1dd 100644 Binary files a/_module/ncs/hench_isrecover.ncs and b/_module/ncs/hench_isrecover.ncs differ diff --git a/_module/ncs/hench_no_recover.ncs b/_module/ncs/hench_no_recover.ncs index 9df1d9d5..3cba3d54 100644 Binary files a/_module/ncs/hench_no_recover.ncs and b/_module/ncs/hench_no_recover.ncs differ diff --git a/_module/ncs/hench_noautounlo.ncs b/_module/ncs/hench_noautounlo.ncs index d6559d27..3e96a3e3 100644 Binary files a/_module/ncs/hench_noautounlo.ncs and b/_module/ncs/hench_noautounlo.ncs differ diff --git a/_module/ncs/hench_noopen_che.ncs b/_module/ncs/hench_noopen_che.ncs index 3f807a01..5342645c 100644 Binary files a/_module/ncs/hench_noopen_che.ncs and b/_module/ncs/hench_noopen_che.ncs differ diff --git a/_module/ncs/hench_noopen_loc.ncs b/_module/ncs/hench_noopen_loc.ncs index 2f55cfe4..8595bace 100644 Binary files a/_module/ncs/hench_noopen_loc.ncs and b/_module/ncs/hench_noopen_loc.ncs differ diff --git a/_module/ncs/hench_noopenches.ncs b/_module/ncs/hench_noopenches.ncs index 534f51ef..6d760416 100644 Binary files a/_module/ncs/hench_noopenches.ncs and b/_module/ncs/hench_noopenches.ncs differ diff --git a/_module/ncs/hench_nopickup.ncs b/_module/ncs/hench_nopickup.ncs index c8107bf8..acef6eb2 100644 Binary files a/_module/ncs/hench_nopickup.ncs and b/_module/ncs/hench_nopickup.ncs differ diff --git a/_module/ncs/hench_norecover.ncs b/_module/ncs/hench_norecover.ncs index 313816fb..5d605654 100644 Binary files a/_module/ncs/hench_norecover.ncs and b/_module/ncs/hench_norecover.ncs differ diff --git a/_module/ncs/hench_notpickup.ncs b/_module/ncs/hench_notpickup.ncs index 93f47145..d4f66aea 100644 Binary files a/_module/ncs/hench_notpickup.ncs and b/_module/ncs/hench_notpickup.ncs differ diff --git a/_module/ncs/hench_o0_heal.ncs b/_module/ncs/hench_o0_heal.ncs index 24e787aa..4ec1199c 100644 Binary files a/_module/ncs/hench_o0_heal.ncs and b/_module/ncs/hench_o0_heal.ncs differ diff --git a/_module/ncs/hench_open_chest.ncs b/_module/ncs/hench_open_chest.ncs index fed36fdd..7f398ad7 100644 Binary files a/_module/ncs/hench_open_chest.ncs and b/_module/ncs/hench_open_chest.ncs differ diff --git a/_module/ncs/hench_open_locks.ncs b/_module/ncs/hench_open_locks.ncs index c52061a9..1805f686 100644 Binary files a/_module/ncs/hench_open_locks.ncs and b/_module/ncs/hench_open_locks.ncs differ diff --git a/_module/ncs/hench_patrol.ncs b/_module/ncs/hench_patrol.ncs index 153ca015..cbb8f28a 100644 Binary files a/_module/ncs/hench_patrol.ncs and b/_module/ncs/hench_patrol.ncs differ diff --git a/_module/ncs/hench_pick_up.ncs b/_module/ncs/hench_pick_up.ncs index 4201272c..ef9bbe8a 100644 Binary files a/_module/ncs/hench_pick_up.ncs and b/_module/ncs/hench_pick_up.ncs differ diff --git a/_module/ncs/hench_recover_tr.ncs b/_module/ncs/hench_recover_tr.ncs index 444f8c58..1a0ffa94 100644 Binary files a/_module/ncs/hench_recover_tr.ncs and b/_module/ncs/hench_recover_tr.ncs differ diff --git a/_module/ncs/hench_summon_no.ncs b/_module/ncs/hench_summon_no.ncs index 5e0c0d28..6fa81684 100644 Binary files a/_module/ncs/hench_summon_no.ncs and b/_module/ncs/hench_summon_no.ncs differ diff --git a/_module/ncs/hench_summon_yes.ncs b/_module/ncs/hench_summon_yes.ncs index abb58f1d..87c0482e 100644 Binary files a/_module/ncs/hench_summon_yes.ncs and b/_module/ncs/hench_summon_yes.ncs differ diff --git a/_module/ncs/henchasgate.ncs b/_module/ncs/henchasgate.ncs index 7f0c9223..4a0e179a 100644 Binary files a/_module/ncs/henchasgate.ncs and b/_module/ncs/henchasgate.ncs differ diff --git a/_module/ncs/henchcast1.ncs b/_module/ncs/henchcast1.ncs index 5180ff25..01998d1a 100644 Binary files a/_module/ncs/henchcast1.ncs and b/_module/ncs/henchcast1.ncs differ diff --git a/_module/ncs/henchcast2.ncs b/_module/ncs/henchcast2.ncs index f4dedc3e..c49890d8 100644 Binary files a/_module/ncs/henchcast2.ncs and b/_module/ncs/henchcast2.ncs differ diff --git a/_module/ncs/henchcastno.ncs b/_module/ncs/henchcastno.ncs index de46dcec..85e7becb 100644 Binary files a/_module/ncs/henchcastno.ncs and b/_module/ncs/henchcastno.ncs differ diff --git a/_module/ncs/henchcastyes.ncs b/_module/ncs/henchcastyes.ncs index e0772853..fe79b7ce 100644 Binary files a/_module/ncs/henchcastyes.ncs and b/_module/ncs/henchcastyes.ncs differ diff --git a/_module/ncs/henchcomp.ncs b/_module/ncs/henchcomp.ncs index 7063ca34..e02219df 100644 Binary files a/_module/ncs/henchcomp.ncs and b/_module/ncs/henchcomp.ncs differ diff --git a/_module/ncs/henchdisdual.ncs b/_module/ncs/henchdisdual.ncs index df04a6b2..c2ca720a 100644 Binary files a/_module/ncs/henchdisdual.ncs and b/_module/ncs/henchdisdual.ncs differ diff --git a/_module/ncs/henchdualdis.ncs b/_module/ncs/henchdualdis.ncs index 459ebaa3..4c670801 100644 Binary files a/_module/ncs/henchdualdis.ncs and b/_module/ncs/henchdualdis.ncs differ diff --git a/_module/ncs/henchdualen.ncs b/_module/ncs/henchdualen.ncs index 72667256..280e7332 100644 Binary files a/_module/ncs/henchdualen.ncs and b/_module/ncs/henchdualen.ncs differ diff --git a/_module/ncs/henchendual.ncs b/_module/ncs/henchendual.ncs index 39032cbf..a924a220 100644 Binary files a/_module/ncs/henchendual.ncs and b/_module/ncs/henchendual.ncs differ diff --git a/_module/ncs/henchenheavy.ncs b/_module/ncs/henchenheavy.ncs index b85d6122..3fc4bdb7 100644 Binary files a/_module/ncs/henchenheavy.ncs and b/_module/ncs/henchenheavy.ncs differ diff --git a/_module/ncs/henchenlight.ncs b/_module/ncs/henchenlight.ncs index 85a66870..b9353350 100644 Binary files a/_module/ncs/henchenlight.ncs and b/_module/ncs/henchenlight.ncs differ diff --git a/_module/ncs/henchfamilar.ncs b/_module/ncs/henchfamilar.ncs index 6398191f..36d5f1bb 100644 Binary files a/_module/ncs/henchfamilar.ncs and b/_module/ncs/henchfamilar.ncs differ diff --git a/_module/ncs/henchhasbbod.ncs b/_module/ncs/henchhasbbod.ncs index 888a7252..519fca3e 100644 Binary files a/_module/ncs/henchhasbbod.ncs and b/_module/ncs/henchhasbbod.ncs differ diff --git a/_module/ncs/henchhaselem.ncs b/_module/ncs/henchhaselem.ncs index b9ba6a30..cd7f1692 100644 Binary files a/_module/ncs/henchhaselem.ncs and b/_module/ncs/henchhaselem.ncs differ diff --git a/_module/ncs/henchhaspfe.ncs b/_module/ncs/henchhaspfe.ncs index 59376b45..c945881c 100644 Binary files a/_module/ncs/henchhaspfe.ncs and b/_module/ncs/henchhaspfe.ncs differ diff --git a/_module/ncs/henchlightdis.ncs b/_module/ncs/henchlightdis.ncs index b88e9f07..7b2cc6fb 100644 Binary files a/_module/ncs/henchlightdis.ncs and b/_module/ncs/henchlightdis.ncs differ diff --git a/_module/ncs/henchlighten.ncs b/_module/ncs/henchlighten.ncs index c748e302..3e79de33 100644 Binary files a/_module/ncs/henchlighten.ncs and b/_module/ncs/henchlighten.ncs differ diff --git a/_module/ncs/henchlvl_arcanta.ncs b/_module/ncs/henchlvl_arcanta.ncs index 8ab4117d..458709ee 100644 Binary files a/_module/ncs/henchlvl_arcanta.ncs and b/_module/ncs/henchlvl_arcanta.ncs differ diff --git a/_module/ncs/henchlvl_assasta.ncs b/_module/ncs/henchlvl_assasta.ncs index 81c316ec..3390b176 100644 Binary files a/_module/ncs/henchlvl_assasta.ncs and b/_module/ncs/henchlvl_assasta.ncs differ diff --git a/_module/ncs/henchlvl_barbta.ncs b/_module/ncs/henchlvl_barbta.ncs index 3c75458c..09d6e385 100644 Binary files a/_module/ncs/henchlvl_barbta.ncs and b/_module/ncs/henchlvl_barbta.ncs differ diff --git a/_module/ncs/henchlvl_bardta.ncs b/_module/ncs/henchlvl_bardta.ncs index ab5cb052..f9f5ffa9 100644 Binary files a/_module/ncs/henchlvl_bardta.ncs and b/_module/ncs/henchlvl_bardta.ncs differ diff --git a/_module/ncs/henchlvl_blackta.ncs b/_module/ncs/henchlvl_blackta.ncs index 0eaecb57..01f799b3 100644 Binary files a/_module/ncs/henchlvl_blackta.ncs and b/_module/ncs/henchlvl_blackta.ncs differ diff --git a/_module/ncs/henchlvl_clercta.ncs b/_module/ncs/henchlvl_clercta.ncs index e0cffee8..81fd882f 100644 Binary files a/_module/ncs/henchlvl_clercta.ncs and b/_module/ncs/henchlvl_clercta.ncs differ diff --git a/_module/ncs/henchlvl_drgdsta.ncs b/_module/ncs/henchlvl_drgdsta.ncs index 86e15c15..5fe463b7 100644 Binary files a/_module/ncs/henchlvl_drgdsta.ncs and b/_module/ncs/henchlvl_drgdsta.ncs differ diff --git a/_module/ncs/henchlvl_druidta.ncs b/_module/ncs/henchlvl_druidta.ncs index bc55f18a..0d14cb3b 100644 Binary files a/_module/ncs/henchlvl_druidta.ncs and b/_module/ncs/henchlvl_druidta.ncs differ diff --git a/_module/ncs/henchlvl_dvnchta.ncs b/_module/ncs/henchlvl_dvnchta.ncs index d93041eb..8db14402 100644 Binary files a/_module/ncs/henchlvl_dvnchta.ncs and b/_module/ncs/henchlvl_dvnchta.ncs differ diff --git a/_module/ncs/henchlvl_dwrfdta.ncs b/_module/ncs/henchlvl_dwrfdta.ncs index a62dc578..4991aae3 100644 Binary files a/_module/ncs/henchlvl_dwrfdta.ncs and b/_module/ncs/henchlvl_dwrfdta.ncs differ diff --git a/_module/ncs/henchlvl_fightta.ncs b/_module/ncs/henchlvl_fightta.ncs index da384d6b..1a2f9717 100644 Binary files a/_module/ncs/henchlvl_fightta.ncs and b/_module/ncs/henchlvl_fightta.ncs differ diff --git a/_module/ncs/henchlvl_harpeta.ncs b/_module/ncs/henchlvl_harpeta.ncs index c50625f8..13216cfa 100644 Binary files a/_module/ncs/henchlvl_harpeta.ncs and b/_module/ncs/henchlvl_harpeta.ncs differ diff --git a/_module/ncs/henchlvl_monkta.ncs b/_module/ncs/henchlvl_monkta.ncs index bc059134..9117dd8c 100644 Binary files a/_module/ncs/henchlvl_monkta.ncs and b/_module/ncs/henchlvl_monkta.ncs differ diff --git a/_module/ncs/henchlvl_palata.ncs b/_module/ncs/henchlvl_palata.ncs index adfeec13..73d53bc4 100644 Binary files a/_module/ncs/henchlvl_palata.ncs and b/_module/ncs/henchlvl_palata.ncs differ diff --git a/_module/ncs/henchlvl_palemta.ncs b/_module/ncs/henchlvl_palemta.ncs index 1147fc02..dd9b9541 100644 Binary files a/_module/ncs/henchlvl_palemta.ncs and b/_module/ncs/henchlvl_palemta.ncs differ diff --git a/_module/ncs/henchlvl_rangeta.ncs b/_module/ncs/henchlvl_rangeta.ncs index c1e004c0..21d0ea32 100644 Binary files a/_module/ncs/henchlvl_rangeta.ncs and b/_module/ncs/henchlvl_rangeta.ncs differ diff --git a/_module/ncs/henchlvl_rogta.ncs b/_module/ncs/henchlvl_rogta.ncs index 8a6e875e..e18ab3a2 100644 Binary files a/_module/ncs/henchlvl_rogta.ncs and b/_module/ncs/henchlvl_rogta.ncs differ diff --git a/_module/ncs/henchlvl_shadota.ncs b/_module/ncs/henchlvl_shadota.ncs index de921ec0..d156e05c 100644 Binary files a/_module/ncs/henchlvl_shadota.ncs and b/_module/ncs/henchlvl_shadota.ncs differ diff --git a/_module/ncs/henchlvl_shiftta.ncs b/_module/ncs/henchlvl_shiftta.ncs index d0c5ee35..e62d45d8 100644 Binary files a/_module/ncs/henchlvl_shiftta.ncs and b/_module/ncs/henchlvl_shiftta.ncs differ diff --git a/_module/ncs/henchlvl_sorceta.ncs b/_module/ncs/henchlvl_sorceta.ncs index 1d2780ce..99c2ea0e 100644 Binary files a/_module/ncs/henchlvl_sorceta.ncs and b/_module/ncs/henchlvl_sorceta.ncs differ diff --git a/_module/ncs/henchlvl_wizarta.ncs b/_module/ncs/henchlvl_wizarta.ncs index 20f83ae5..b95d95f6 100644 Binary files a/_module/ncs/henchlvl_wizarta.ncs and b/_module/ncs/henchlvl_wizarta.ncs differ diff --git a/_module/ncs/henchlvl_wpnmsta.ncs b/_module/ncs/henchlvl_wpnmsta.ncs index 8bbef085..115c8ab2 100644 Binary files a/_module/ncs/henchlvl_wpnmsta.ncs and b/_module/ncs/henchlvl_wpnmsta.ncs differ diff --git a/_module/ncs/henchrange0.ncs b/_module/ncs/henchrange0.ncs index 29a4811e..1aba9f59 100644 Binary files a/_module/ncs/henchrange0.ncs and b/_module/ncs/henchrange0.ncs differ diff --git a/_module/ncs/henchrange1.ncs b/_module/ncs/henchrange1.ncs index 56487444..0c331bb3 100644 Binary files a/_module/ncs/henchrange1.ncs and b/_module/ncs/henchrange1.ncs differ diff --git a/_module/ncs/henchrange2.ncs b/_module/ncs/henchrange2.ncs index 09980fde..09a547fe 100644 Binary files a/_module/ncs/henchrange2.ncs and b/_module/ncs/henchrange2.ncs differ diff --git a/_module/ncs/henchrange3.ncs b/_module/ncs/henchrange3.ncs index ccba6f94..e2f4371b 100644 Binary files a/_module/ncs/henchrange3.ncs and b/_module/ncs/henchrange3.ncs differ diff --git a/_module/ncs/henchrange4.ncs b/_module/ncs/henchrange4.ncs index 7f49406a..78f610a6 100644 Binary files a/_module/ncs/henchrange4.ncs and b/_module/ncs/henchrange4.ncs differ diff --git a/_module/ncs/henchspell0.ncs b/_module/ncs/henchspell0.ncs index e180981e..6755df64 100644 Binary files a/_module/ncs/henchspell0.ncs and b/_module/ncs/henchspell0.ncs differ diff --git a/_module/ncs/henchspell1.ncs b/_module/ncs/henchspell1.ncs index cf0dcae2..78511a48 100644 Binary files a/_module/ncs/henchspell1.ncs and b/_module/ncs/henchspell1.ncs differ diff --git a/_module/ncs/henchspell2.ncs b/_module/ncs/henchspell2.ncs index cf312f52..814fa261 100644 Binary files a/_module/ncs/henchspell2.ncs and b/_module/ncs/henchspell2.ncs differ diff --git a/_module/ncs/henchspell3.ncs b/_module/ncs/henchspell3.ncs index c074f9d6..afb54faf 100644 Binary files a/_module/ncs/henchspell3.ncs and b/_module/ncs/henchspell3.ncs differ diff --git a/_module/ncs/het_getvar.ncs b/_module/ncs/het_getvar.ncs index 2a15fcc6..33e44fa2 100644 Binary files a/_module/ncs/het_getvar.ncs and b/_module/ncs/het_getvar.ncs differ diff --git a/_module/ncs/hetman_done.ncs b/_module/ncs/hetman_done.ncs index ebd91155..88ef05a9 100644 Binary files a/_module/ncs/hetman_done.ncs and b/_module/ncs/hetman_done.ncs differ diff --git a/_module/ncs/hetman_var1.ncs b/_module/ncs/hetman_var1.ncs index e969d3b7..196573d4 100644 Binary files a/_module/ncs/hetman_var1.ncs and b/_module/ncs/hetman_var1.ncs differ diff --git a/_module/ncs/hif_onclientente.ncs b/_module/ncs/hif_onclientente.ncs index 6c13493f..4a553238 100644 Binary files a/_module/ncs/hif_onclientente.ncs and b/_module/ncs/hif_onclientente.ncs differ diff --git a/_module/ncs/hls_wandlab.ncs b/_module/ncs/hls_wandlab.ncs index 5e1fd9a9..60fc720f 100644 Binary files a/_module/ncs/hls_wandlab.ncs and b/_module/ncs/hls_wandlab.ncs differ diff --git a/_module/ncs/ice_slip.ncs b/_module/ncs/ice_slip.ncs index dcf772ae..c0047a95 100644 Binary files a/_module/ncs/ice_slip.ncs and b/_module/ncs/ice_slip.ncs differ diff --git a/_module/ncs/innerkeep_enter.ncs b/_module/ncs/innerkeep_enter.ncs index 0aa7d933..7883b251 100644 Binary files a/_module/ncs/innerkeep_enter.ncs and b/_module/ncs/innerkeep_enter.ncs differ diff --git a/_module/ncs/irda_done.ncs b/_module/ncs/irda_done.ncs index a609df01..2f9ac0b8 100644 Binary files a/_module/ncs/irda_done.ncs and b/_module/ncs/irda_done.ncs differ diff --git a/_module/ncs/irda_getvar1.ncs b/_module/ncs/irda_getvar1.ncs index a609df01..2f9ac0b8 100644 Binary files a/_module/ncs/irda_getvar1.ncs and b/_module/ncs/irda_getvar1.ncs differ diff --git a/_module/ncs/irda_port_nas.ncs b/_module/ncs/irda_port_nas.ncs index ca204303..6c1e0410 100644 Binary files a/_module/ncs/irda_port_nas.ncs and b/_module/ncs/irda_port_nas.ncs differ diff --git a/_module/ncs/irda_port_nasout.ncs b/_module/ncs/irda_port_nasout.ncs index d5f1941b..f89e2105 100644 Binary files a/_module/ncs/irda_port_nasout.ncs and b/_module/ncs/irda_port_nasout.ncs differ diff --git a/_module/ncs/irda_port_sarum.ncs b/_module/ncs/irda_port_sarum.ncs index bf89a716..08e656c7 100644 Binary files a/_module/ncs/irda_port_sarum.ncs and b/_module/ncs/irda_port_sarum.ncs differ diff --git a/_module/ncs/irda_port_southp.ncs b/_module/ncs/irda_port_southp.ncs index 1a81266c..0fb0917d 100644 Binary files a/_module/ncs/irda_port_southp.ncs and b/_module/ncs/irda_port_southp.ncs differ diff --git a/_module/ncs/irda_setvar.ncs b/_module/ncs/irda_setvar.ncs index d8f1114b..774867b5 100644 Binary files a/_module/ncs/irda_setvar.ncs and b/_module/ncs/irda_setvar.ncs differ diff --git a/_module/ncs/irda_spoken2.ncs b/_module/ncs/irda_spoken2.ncs index fe5bf643..7f355207 100644 Binary files a/_module/ncs/irda_spoken2.ncs and b/_module/ncs/irda_spoken2.ncs differ diff --git a/_module/ncs/irda_start.ncs b/_module/ncs/irda_start.ncs index c4a0a68f..db51e6e1 100644 Binary files a/_module/ncs/irda_start.ncs and b/_module/ncs/irda_start.ncs differ diff --git a/_module/ncs/it_guardstone.ncs b/_module/ncs/it_guardstone.ncs index e6966b8f..7c845b99 100644 Binary files a/_module/ncs/it_guardstone.ncs and b/_module/ncs/it_guardstone.ncs differ diff --git a/_module/ncs/it_imprisonments.ncs b/_module/ncs/it_imprisonments.ncs index 70899518..36abc46d 100644 Binary files a/_module/ncs/it_imprisonments.ncs and b/_module/ncs/it_imprisonments.ncs differ diff --git a/_module/ncs/item_enter_cho.ncs b/_module/ncs/item_enter_cho.ncs index 37b78323..9e5e9f6d 100644 Binary files a/_module/ncs/item_enter_cho.ncs and b/_module/ncs/item_enter_cho.ncs differ diff --git a/_module/ncs/jacob_cho_10_ck.ncs b/_module/ncs/jacob_cho_10_ck.ncs index 66f0d629..08d637be 100644 Binary files a/_module/ncs/jacob_cho_10_ck.ncs and b/_module/ncs/jacob_cho_10_ck.ncs differ diff --git a/_module/ncs/jade_spawn.ncs b/_module/ncs/jade_spawn.ncs index e869408e..f91df758 100644 Binary files a/_module/ncs/jade_spawn.ncs and b/_module/ncs/jade_spawn.ncs differ diff --git a/_module/ncs/jail_align_shift.ncs b/_module/ncs/jail_align_shift.ncs index 164dd8ec..4099760d 100644 Binary files a/_module/ncs/jail_align_shift.ncs and b/_module/ncs/jail_align_shift.ncs differ diff --git a/_module/ncs/jail_set_var.ncs b/_module/ncs/jail_set_var.ncs index c01546c2..352d86c0 100644 Binary files a/_module/ncs/jail_set_var.ncs and b/_module/ncs/jail_set_var.ncs differ diff --git a/_module/ncs/jailor_done.ncs b/_module/ncs/jailor_done.ncs index fb844d17..5b3ef787 100644 Binary files a/_module/ncs/jailor_done.ncs and b/_module/ncs/jailor_done.ncs differ diff --git a/_module/ncs/jailor_spoke2.ncs b/_module/ncs/jailor_spoke2.ncs index 3ff4471c..dbcfcfdf 100644 Binary files a/_module/ncs/jailor_spoke2.ncs and b/_module/ncs/jailor_spoke2.ncs differ diff --git a/_module/ncs/joril_3_ck.ncs b/_module/ncs/joril_3_ck.ncs index bab3ddfa..0220e94c 100644 Binary files a/_module/ncs/joril_3_ck.ncs and b/_module/ncs/joril_3_ck.ncs differ diff --git a/_module/ncs/joril_see_pc.ncs b/_module/ncs/joril_see_pc.ncs index 067e20f0..7aaacb21 100644 Binary files a/_module/ncs/joril_see_pc.ncs and b/_module/ncs/joril_see_pc.ncs differ diff --git a/_module/ncs/kdbkeygen.ncs b/_module/ncs/kdbkeygen.ncs index f2bec211..f92e4778 100644 Binary files a/_module/ncs/kdbkeygen.ncs and b/_module/ncs/kdbkeygen.ncs differ diff --git a/_module/ncs/king_give_1.ncs b/_module/ncs/king_give_1.ncs index df1fdaf3..e897e403 100644 Binary files a/_module/ncs/king_give_1.ncs and b/_module/ncs/king_give_1.ncs differ diff --git a/_module/ncs/king_give_3.ncs b/_module/ncs/king_give_3.ncs index b0b8ed85..9727afdd 100644 Binary files a/_module/ncs/king_give_3.ncs and b/_module/ncs/king_give_3.ncs differ diff --git a/_module/ncs/king_seal_ck.ncs b/_module/ncs/king_seal_ck.ncs index e0624b14..ba3a7584 100644 Binary files a/_module/ncs/king_seal_ck.ncs and b/_module/ncs/king_seal_ck.ncs differ diff --git a/_module/ncs/king_seal_ck2.ncs b/_module/ncs/king_seal_ck2.ncs index 14f5809d..69fcf690 100644 Binary files a/_module/ncs/king_seal_ck2.ncs and b/_module/ncs/king_seal_ck2.ncs differ diff --git a/_module/ncs/king_sit.ncs b/_module/ncs/king_sit.ncs index 73633e4d..9bda952d 100644 Binary files a/_module/ncs/king_sit.ncs and b/_module/ncs/king_sit.ncs differ diff --git a/_module/ncs/king_sithb.ncs b/_module/ncs/king_sithb.ncs index 849d9f08..588548c0 100644 Binary files a/_module/ncs/king_sithb.ncs and b/_module/ncs/king_sithb.ncs differ diff --git a/_module/ncs/king_take_note.ncs b/_module/ncs/king_take_note.ncs index 2a0f193f..007d430b 100644 Binary files a/_module/ncs/king_take_note.ncs and b/_module/ncs/king_take_note.ncs differ diff --git a/_module/ncs/kingannounce.ncs b/_module/ncs/kingannounce.ncs index 3e0db829..37b48fe3 100644 Binary files a/_module/ncs/kingannounce.ncs and b/_module/ncs/kingannounce.ncs differ diff --git a/_module/ncs/kingreward3.ncs b/_module/ncs/kingreward3.ncs index 9987631f..73f3f5c3 100644 Binary files a/_module/ncs/kingreward3.ncs and b/_module/ncs/kingreward3.ncs differ diff --git a/_module/ncs/kingspeak.ncs b/_module/ncs/kingspeak.ncs index 2f6677d1..41e53a60 100644 Binary files a/_module/ncs/kingspeak.ncs and b/_module/ncs/kingspeak.ncs differ diff --git a/_module/ncs/kpb_credit_token.ncs b/_module/ncs/kpb_credit_token.ncs index bff1d1b3..dbada12d 100644 Binary files a/_module/ncs/kpb_credit_token.ncs and b/_module/ncs/kpb_credit_token.ncs differ diff --git a/_module/ncs/kpb_deposit100.ncs b/_module/ncs/kpb_deposit100.ncs index 1b01f074..a49e5805 100644 Binary files a/_module/ncs/kpb_deposit100.ncs and b/_module/ncs/kpb_deposit100.ncs differ diff --git a/_module/ncs/kpb_deposit1000.ncs b/_module/ncs/kpb_deposit1000.ncs index 9774fcb4..c88cad0e 100644 Binary files a/_module/ncs/kpb_deposit1000.ncs and b/_module/ncs/kpb_deposit1000.ncs differ diff --git a/_module/ncs/kpb_deposit10000.ncs b/_module/ncs/kpb_deposit10000.ncs index aedfd894..a91c8177 100644 Binary files a/_module/ncs/kpb_deposit10000.ncs and b/_module/ncs/kpb_deposit10000.ncs differ diff --git a/_module/ncs/kpb_globals.ncs b/_module/ncs/kpb_globals.ncs index f88470cd..448165c1 100644 Binary files a/_module/ncs/kpb_globals.ncs and b/_module/ncs/kpb_globals.ncs differ diff --git a/_module/ncs/kpb_int_balance.ncs b/_module/ncs/kpb_int_balance.ncs index 30758039..1b66df4d 100644 Binary files a/_module/ncs/kpb_int_balance.ncs and b/_module/ncs/kpb_int_balance.ncs differ diff --git a/_module/ncs/kpb_loan_balance.ncs b/_module/ncs/kpb_loan_balance.ncs index 0adacce1..da38b870 100644 Binary files a/_module/ncs/kpb_loan_balance.ncs and b/_module/ncs/kpb_loan_balance.ncs differ diff --git a/_module/ncs/kpb_loan_chkhigh.ncs b/_module/ncs/kpb_loan_chkhigh.ncs index 390faa08..eda9246c 100644 Binary files a/_module/ncs/kpb_loan_chkhigh.ncs and b/_module/ncs/kpb_loan_chkhigh.ncs differ diff --git a/_module/ncs/kpb_loan_chklow.ncs b/_module/ncs/kpb_loan_chklow.ncs index aad9a067..afcba307 100644 Binary files a/_module/ncs/kpb_loan_chklow.ncs and b/_module/ncs/kpb_loan_chklow.ncs differ diff --git a/_module/ncs/kpb_loan_chkmed.ncs b/_module/ncs/kpb_loan_chkmed.ncs index 18176730..1c9248e2 100644 Binary files a/_module/ncs/kpb_loan_chkmed.ncs and b/_module/ncs/kpb_loan_chkmed.ncs differ diff --git a/_module/ncs/kpb_loan_off.ncs b/_module/ncs/kpb_loan_off.ncs index 6199cb43..09665d0b 100644 Binary files a/_module/ncs/kpb_loan_off.ncs and b/_module/ncs/kpb_loan_off.ncs differ diff --git a/_module/ncs/kpb_loan_on.ncs b/_module/ncs/kpb_loan_on.ncs index 579ba8e5..4c04d83b 100644 Binary files a/_module/ncs/kpb_loan_on.ncs and b/_module/ncs/kpb_loan_on.ncs differ diff --git a/_module/ncs/kpb_payloan_all.ncs b/_module/ncs/kpb_payloan_all.ncs index 8e610b82..f9673f0f 100644 Binary files a/_module/ncs/kpb_payloan_all.ncs and b/_module/ncs/kpb_payloan_all.ncs differ diff --git a/_module/ncs/kpb_takeloan100.ncs b/_module/ncs/kpb_takeloan100.ncs index d5e53438..7cef2360 100644 Binary files a/_module/ncs/kpb_takeloan100.ncs and b/_module/ncs/kpb_takeloan100.ncs differ diff --git a/_module/ncs/kpb_takeloan1000.ncs b/_module/ncs/kpb_takeloan1000.ncs index 49b5652d..b8dc2edc 100644 Binary files a/_module/ncs/kpb_takeloan1000.ncs and b/_module/ncs/kpb_takeloan1000.ncs differ diff --git a/_module/ncs/kpb_takeloan5000.ncs b/_module/ncs/kpb_takeloan5000.ncs index d062bd98..4e035fd2 100644 Binary files a/_module/ncs/kpb_takeloan5000.ncs and b/_module/ncs/kpb_takeloan5000.ncs differ diff --git a/_module/ncs/kpb_tkeloan10000.ncs b/_module/ncs/kpb_tkeloan10000.ncs index c18949a2..095c8cd0 100644 Binary files a/_module/ncs/kpb_tkeloan10000.ncs and b/_module/ncs/kpb_tkeloan10000.ncs differ diff --git a/_module/ncs/kpb_withdraw100.ncs b/_module/ncs/kpb_withdraw100.ncs index 2ce860ee..330c30e7 100644 Binary files a/_module/ncs/kpb_withdraw100.ncs and b/_module/ncs/kpb_withdraw100.ncs differ diff --git a/_module/ncs/kpb_withdraw1000.ncs b/_module/ncs/kpb_withdraw1000.ncs index 21209a7a..12ec3915 100644 Binary files a/_module/ncs/kpb_withdraw1000.ncs and b/_module/ncs/kpb_withdraw1000.ncs differ diff --git a/_module/ncs/kpb_withdraw_all.ncs b/_module/ncs/kpb_withdraw_all.ncs index 0bcb26b3..c3f5bcd8 100644 Binary files a/_module/ncs/kpb_withdraw_all.ncs and b/_module/ncs/kpb_withdraw_all.ncs differ diff --git a/_module/ncs/kpb_wthdraw10000.ncs b/_module/ncs/kpb_wthdraw10000.ncs index 5363bda5..95dce4b8 100644 Binary files a/_module/ncs/kpb_wthdraw10000.ncs and b/_module/ncs/kpb_wthdraw10000.ncs differ diff --git a/_module/ncs/krog_quest.ncs b/_module/ncs/krog_quest.ncs index 1db1bd54..a5ff07b7 100644 Binary files a/_module/ncs/krog_quest.ncs and b/_module/ncs/krog_quest.ncs differ diff --git a/_module/ncs/kylith_ondeath.ncs b/_module/ncs/kylith_ondeath.ncs index c2aacfa3..90e40bfc 100644 Binary files a/_module/ncs/kylith_ondeath.ncs and b/_module/ncs/kylith_ondeath.ncs differ diff --git a/_module/ncs/l_sot.ncs b/_module/ncs/l_sot.ncs index 1ec4378a..0e5d31e0 100644 Binary files a/_module/ncs/l_sot.ncs and b/_module/ncs/l_sot.ncs differ diff --git a/_module/ncs/leveltake1.ncs b/_module/ncs/leveltake1.ncs index b01918b5..31005334 100644 Binary files a/_module/ncs/leveltake1.ncs and b/_module/ncs/leveltake1.ncs differ diff --git a/_module/ncs/leveltake10.ncs b/_module/ncs/leveltake10.ncs index 88041a64..314db8ae 100644 Binary files a/_module/ncs/leveltake10.ncs and b/_module/ncs/leveltake10.ncs differ diff --git a/_module/ncs/leveltake5.ncs b/_module/ncs/leveltake5.ncs index 4c32454f..06033801 100644 Binary files a/_module/ncs/leveltake5.ncs and b/_module/ncs/leveltake5.ncs differ diff --git a/_module/ncs/lever_combo1.ncs b/_module/ncs/lever_combo1.ncs index 0076bc5e..1a334c8d 100644 Binary files a/_module/ncs/lever_combo1.ncs and b/_module/ncs/lever_combo1.ncs differ diff --git a/_module/ncs/lmpperfact01.ncs b/_module/ncs/lmpperfact01.ncs index 8f065033..4fab3211 100644 Binary files a/_module/ncs/lmpperfact01.ncs and b/_module/ncs/lmpperfact01.ncs differ diff --git a/_module/ncs/lmpperfact04.ncs b/_module/ncs/lmpperfact04.ncs index 6b466afe..b9d3fab3 100644 Binary files a/_module/ncs/lmpperfact04.ncs and b/_module/ncs/lmpperfact04.ncs differ diff --git a/_module/ncs/lmpperfactrefund.ncs b/_module/ncs/lmpperfactrefund.ncs index a8fc074a..0c70a8bd 100644 Binary files a/_module/ncs/lmpperfactrefund.ncs and b/_module/ncs/lmpperfactrefund.ncs differ diff --git a/_module/ncs/lmpperfacttheft.ncs b/_module/ncs/lmpperfacttheft.ncs index f50780cc..c24dcaa6 100644 Binary files a/_module/ncs/lmpperfacttheft.ncs and b/_module/ncs/lmpperfacttheft.ncs differ diff --git a/_module/ncs/lmpperfbow.ncs b/_module/ncs/lmpperfbow.ncs index d2eadcb0..d45b67a2 100644 Binary files a/_module/ncs/lmpperfbow.ncs and b/_module/ncs/lmpperfbow.ncs differ diff --git a/_module/ncs/lmpperfcond11.ncs b/_module/ncs/lmpperfcond11.ncs index 0e880c73..e4ade2aa 100644 Binary files a/_module/ncs/lmpperfcond11.ncs and b/_module/ncs/lmpperfcond11.ncs differ diff --git a/_module/ncs/lmpperfcond12.ncs b/_module/ncs/lmpperfcond12.ncs index f4f5f8f7..126f03d7 100644 Binary files a/_module/ncs/lmpperfcond12.ncs and b/_module/ncs/lmpperfcond12.ncs differ diff --git a/_module/ncs/lmpperfcond2.ncs b/_module/ncs/lmpperfcond2.ncs index 48ced857..019cf865 100644 Binary files a/_module/ncs/lmpperfcond2.ncs and b/_module/ncs/lmpperfcond2.ncs differ diff --git a/_module/ncs/lmpperfcond3.ncs b/_module/ncs/lmpperfcond3.ncs index da0dbfed..4723e63c 100644 Binary files a/_module/ncs/lmpperfcond3.ncs and b/_module/ncs/lmpperfcond3.ncs differ diff --git a/_module/ncs/lmpperfcond41.ncs b/_module/ncs/lmpperfcond41.ncs index 2bfd5acf..6f682c12 100644 Binary files a/_module/ncs/lmpperfcond41.ncs and b/_module/ncs/lmpperfcond41.ncs differ diff --git a/_module/ncs/lmpperfcond42.ncs b/_module/ncs/lmpperfcond42.ncs index cd7aef2a..4942119c 100644 Binary files a/_module/ncs/lmpperfcond42.ncs and b/_module/ncs/lmpperfcond42.ncs differ diff --git a/_module/ncs/lmpperfcond5.ncs b/_module/ncs/lmpperfcond5.ncs index 8e45476c..f755596c 100644 Binary files a/_module/ncs/lmpperfcond5.ncs and b/_module/ncs/lmpperfcond5.ncs differ diff --git a/_module/ncs/lmpperfcondperf.ncs b/_module/ncs/lmpperfcondperf.ncs index 61e5d940..3d3eee81 100644 Binary files a/_module/ncs/lmpperfcondperf.ncs and b/_module/ncs/lmpperfcondperf.ncs differ diff --git a/_module/ncs/lmpperfcondtheft.ncs b/_module/ncs/lmpperfcondtheft.ncs index 1bdec408..cdb80b6c 100644 Binary files a/_module/ncs/lmpperfcondtheft.ncs and b/_module/ncs/lmpperfcondtheft.ncs differ diff --git a/_module/ncs/lmpperfconvabort.ncs b/_module/ncs/lmpperfconvabort.ncs index d04c92e7..ca17281e 100644 Binary files a/_module/ncs/lmpperfconvabort.ncs and b/_module/ncs/lmpperfconvabort.ncs differ diff --git a/_module/ncs/lmpperffacepc.ncs b/_module/ncs/lmpperffacepc.ncs index df9962ea..9d145926 100644 Binary files a/_module/ncs/lmpperffacepc.ncs and b/_module/ncs/lmpperffacepc.ncs differ diff --git a/_module/ncs/lmpperfreset02.ncs b/_module/ncs/lmpperfreset02.ncs index 2fbb6b80..208382ae 100644 Binary files a/_module/ncs/lmpperfreset02.ncs and b/_module/ncs/lmpperfreset02.ncs differ diff --git a/_module/ncs/lmpperfreset03.ncs b/_module/ncs/lmpperfreset03.ncs index 2fbb6b80..208382ae 100644 Binary files a/_module/ncs/lmpperfreset03.ncs and b/_module/ncs/lmpperfreset03.ncs differ diff --git a/_module/ncs/lmpperfreset04.ncs b/_module/ncs/lmpperfreset04.ncs index 9d5ed0c5..828321de 100644 Binary files a/_module/ncs/lmpperfreset04.ncs and b/_module/ncs/lmpperfreset04.ncs differ diff --git a/_module/ncs/lmpperfreset05.ncs b/_module/ncs/lmpperfreset05.ncs index 2fbb6b80..208382ae 100644 Binary files a/_module/ncs/lmpperfreset05.ncs and b/_module/ncs/lmpperfreset05.ncs differ diff --git a/_module/ncs/lmpperfspawn.ncs b/_module/ncs/lmpperfspawn.ncs index 6a0aae50..52e21356 100644 Binary files a/_module/ncs/lmpperfspawn.ncs and b/_module/ncs/lmpperfspawn.ncs differ diff --git a/_module/ncs/lmpperfstartperf.ncs b/_module/ncs/lmpperfstartperf.ncs index d682094d..03091c90 100644 Binary files a/_module/ncs/lmpperfstartperf.ncs and b/_module/ncs/lmpperfstartperf.ncs differ diff --git a/_module/ncs/lmpperfterm.ncs b/_module/ncs/lmpperfterm.ncs index 24ca70c9..cac6e122 100644 Binary files a/_module/ncs/lmpperfterm.ncs and b/_module/ncs/lmpperfterm.ncs differ diff --git a/_module/ncs/lmpperfuser.ncs b/_module/ncs/lmpperfuser.ncs index 7ea7c74d..f01fdf9a 100644 Binary files a/_module/ncs/lmpperfuser.ncs and b/_module/ncs/lmpperfuser.ncs differ diff --git a/_module/ncs/lomil_ck_eggs.ncs b/_module/ncs/lomil_ck_eggs.ncs index 964e9333..b6feabe0 100644 Binary files a/_module/ncs/lomil_ck_eggs.ncs and b/_module/ncs/lomil_ck_eggs.ncs differ diff --git a/_module/ncs/lomil_end.ncs b/_module/ncs/lomil_end.ncs index 10237a44..c4bb1a0c 100644 Binary files a/_module/ncs/lomil_end.ncs and b/_module/ncs/lomil_end.ncs differ diff --git a/_module/ncs/lomil_end2.ncs b/_module/ncs/lomil_end2.ncs index c2dce0cd..62228959 100644 Binary files a/_module/ncs/lomil_end2.ncs and b/_module/ncs/lomil_end2.ncs differ diff --git a/_module/ncs/lomil_getvar1.ncs b/_module/ncs/lomil_getvar1.ncs index 26a27b46..27476486 100644 Binary files a/_module/ncs/lomil_getvar1.ncs and b/_module/ncs/lomil_getvar1.ncs differ diff --git a/_module/ncs/lomil_getvar2.ncs b/_module/ncs/lomil_getvar2.ncs index 7912256b..0c4ff63e 100644 Binary files a/_module/ncs/lomil_getvar2.ncs and b/_module/ncs/lomil_getvar2.ncs differ diff --git a/_module/ncs/lose_xp_quest_co.ncs b/_module/ncs/lose_xp_quest_co.ncs index 731aadea..2b38dac1 100644 Binary files a/_module/ncs/lose_xp_quest_co.ncs and b/_module/ncs/lose_xp_quest_co.ncs differ diff --git a/_module/ncs/lose_xp_quest_en.ncs b/_module/ncs/lose_xp_quest_en.ncs index 011f07ce..ca938fad 100644 Binary files a/_module/ncs/lose_xp_quest_en.ncs and b/_module/ncs/lose_xp_quest_en.ncs differ diff --git a/_module/ncs/lose_xp_quest_oc.ncs b/_module/ncs/lose_xp_quest_oc.ncs index 38b9d499..b93a8559 100644 Binary files a/_module/ncs/lose_xp_quest_oc.ncs and b/_module/ncs/lose_xp_quest_oc.ncs differ diff --git a/_module/ncs/lostplayer_ck1.ncs b/_module/ncs/lostplayer_ck1.ncs index 83ba9f13..e543f6b0 100644 Binary files a/_module/ncs/lostplayer_ck1.ncs and b/_module/ncs/lostplayer_ck1.ncs differ diff --git a/_module/ncs/lostplayer_ck2.ncs b/_module/ncs/lostplayer_ck2.ncs index 40524b69..171e6dab 100644 Binary files a/_module/ncs/lostplayer_ck2.ncs and b/_module/ncs/lostplayer_ck2.ncs differ diff --git a/_module/ncs/lostplayer_ck3.ncs b/_module/ncs/lostplayer_ck3.ncs index f0e36295..5eb8a534 100644 Binary files a/_module/ncs/lostplayer_ck3.ncs and b/_module/ncs/lostplayer_ck3.ncs differ diff --git a/_module/ncs/lostplayer_ck4.ncs b/_module/ncs/lostplayer_ck4.ncs index 87517375..4e4916df 100644 Binary files a/_module/ncs/lostplayer_ck4.ncs and b/_module/ncs/lostplayer_ck4.ncs differ diff --git a/_module/ncs/lts_ambplc_used.ncs b/_module/ncs/lts_ambplc_used.ncs index 8ac1b6a6..8b090527 100644 Binary files a/_module/ncs/lts_ambplc_used.ncs and b/_module/ncs/lts_ambplc_used.ncs differ diff --git a/_module/ncs/lucy_ck_lev1.ncs b/_module/ncs/lucy_ck_lev1.ncs index bab3ddfa..0220e94c 100644 Binary files a/_module/ncs/lucy_ck_lev1.ncs and b/_module/ncs/lucy_ck_lev1.ncs differ diff --git a/_module/ncs/magic_user_ck.ncs b/_module/ncs/magic_user_ck.ncs index c6b74141..dbabf27a 100644 Binary files a/_module/ncs/magic_user_ck.ncs and b/_module/ncs/magic_user_ck.ncs differ diff --git a/_module/ncs/mail_body.ncs b/_module/ncs/mail_body.ncs index b9fb4a33..8158b1bd 100644 Binary files a/_module/ncs/mail_body.ncs and b/_module/ncs/mail_body.ncs differ diff --git a/_module/ncs/mail_conv_bckmsg.ncs b/_module/ncs/mail_conv_bckmsg.ncs index 567bcb9f..75f3f37a 100644 Binary files a/_module/ncs/mail_conv_bckmsg.ncs and b/_module/ncs/mail_conv_bckmsg.ncs differ diff --git a/_module/ncs/mail_conv_greet.ncs b/_module/ncs/mail_conv_greet.ncs index b532060a..5d13484d 100644 Binary files a/_module/ncs/mail_conv_greet.ncs and b/_module/ncs/mail_conv_greet.ncs differ diff --git a/_module/ncs/mail_conv_mblist.ncs b/_module/ncs/mail_conv_mblist.ncs index fe5509cc..b3f4aaa9 100644 Binary files a/_module/ncs/mail_conv_mblist.ncs and b/_module/ncs/mail_conv_mblist.ncs differ diff --git a/_module/ncs/mail_conv_msgcnt.ncs b/_module/ncs/mail_conv_msgcnt.ncs index f980aeee..9969eabd 100644 Binary files a/_module/ncs/mail_conv_msgcnt.ncs and b/_module/ncs/mail_conv_msgcnt.ncs differ diff --git a/_module/ncs/mail_conv_msglst.ncs b/_module/ncs/mail_conv_msglst.ncs index 3dda3e29..0ca87503 100644 Binary files a/_module/ncs/mail_conv_msglst.ncs and b/_module/ncs/mail_conv_msglst.ncs differ diff --git a/_module/ncs/mail_conv_next.ncs b/_module/ncs/mail_conv_next.ncs index ae6e1a87..8dd0bc0e 100644 Binary files a/_module/ncs/mail_conv_next.ncs and b/_module/ncs/mail_conv_next.ncs differ diff --git a/_module/ncs/mail_conv_ngreet.ncs b/_module/ncs/mail_conv_ngreet.ncs index f02e48d3..7dfca2dd 100644 Binary files a/_module/ncs/mail_conv_ngreet.ncs and b/_module/ncs/mail_conv_ngreet.ncs differ diff --git a/_module/ncs/mail_conv_nxtmsg.ncs b/_module/ncs/mail_conv_nxtmsg.ncs index ae6e1a87..8dd0bc0e 100644 Binary files a/_module/ncs/mail_conv_nxtmsg.ncs and b/_module/ncs/mail_conv_nxtmsg.ncs differ diff --git a/_module/ncs/mail_conv_prev.ncs b/_module/ncs/mail_conv_prev.ncs index c9c98e1d..6f2c869e 100644 Binary files a/_module/ncs/mail_conv_prev.ncs and b/_module/ncs/mail_conv_prev.ncs differ diff --git a/_module/ncs/mail_conv_showbm.ncs b/_module/ncs/mail_conv_showbm.ncs index 8ee7593a..b0f7191c 100644 Binary files a/_module/ncs/mail_conv_showbm.ncs and b/_module/ncs/mail_conv_showbm.ncs differ diff --git a/_module/ncs/mail_conv_shown.ncs b/_module/ncs/mail_conv_shown.ncs index b4c89b9a..e0304ab7 100644 Binary files a/_module/ncs/mail_conv_shown.ncs and b/_module/ncs/mail_conv_shown.ncs differ diff --git a/_module/ncs/mail_conv_shownm.ncs b/_module/ncs/mail_conv_shownm.ncs index f484d62c..ca40de2a 100644 Binary files a/_module/ncs/mail_conv_shownm.ncs and b/_module/ncs/mail_conv_shownm.ncs differ diff --git a/_module/ncs/mail_conv_showp.ncs b/_module/ncs/mail_conv_showp.ncs index fe22b606..9c31bfe4 100644 Binary files a/_module/ncs/mail_conv_showp.ncs and b/_module/ncs/mail_conv_showp.ncs differ diff --git a/_module/ncs/mail_convstart.ncs b/_module/ncs/mail_convstart.ncs index e35ac0c5..b092d3a5 100644 Binary files a/_module/ncs/mail_convstart.ncs and b/_module/ncs/mail_convstart.ncs differ diff --git a/_module/ncs/mail_deletemsg.ncs b/_module/ncs/mail_deletemsg.ncs index 2c9cbda6..698ca00e 100644 Binary files a/_module/ncs/mail_deletemsg.ncs and b/_module/ncs/mail_deletemsg.ncs differ diff --git a/_module/ncs/mail_hasmb.ncs b/_module/ncs/mail_hasmb.ncs index ad950299..65f8d8a3 100644 Binary files a/_module/ncs/mail_hasmb.ncs and b/_module/ncs/mail_hasmb.ncs differ diff --git a/_module/ncs/mail_hasnomb.ncs b/_module/ncs/mail_hasnomb.ncs index 37d5b11f..e3efc408 100644 Binary files a/_module/ncs/mail_hasnomb.ncs and b/_module/ncs/mail_hasnomb.ncs differ diff --git a/_module/ncs/mail_listenoff.ncs b/_module/ncs/mail_listenoff.ncs index 97fb2d18..ab8b8c99 100644 Binary files a/_module/ncs/mail_listenoff.ncs and b/_module/ncs/mail_listenoff.ncs differ diff --git a/_module/ncs/mail_listenon.ncs b/_module/ncs/mail_listenon.ncs index 2093ee52..e02b5926 100644 Binary files a/_module/ncs/mail_listenon.ncs and b/_module/ncs/mail_listenon.ncs differ diff --git a/_module/ncs/mail_markunread.ncs b/_module/ncs/mail_markunread.ncs index 12b0a626..33489910 100644 Binary files a/_module/ncs/mail_markunread.ncs and b/_module/ncs/mail_markunread.ncs differ diff --git a/_module/ncs/mail_mb_config.ncs b/_module/ncs/mail_mb_config.ncs index 2699343c..dd77a5cf 100644 Binary files a/_module/ncs/mail_mb_config.ncs and b/_module/ncs/mail_mb_config.ncs differ diff --git a/_module/ncs/mail_mb_display.ncs b/_module/ncs/mail_mb_display.ncs index 06d66ac8..7b6c0b85 100644 Binary files a/_module/ncs/mail_mb_display.ncs and b/_module/ncs/mail_mb_display.ncs differ diff --git a/_module/ncs/mail_mb_setup.ncs b/_module/ncs/mail_mb_setup.ncs index 1f8dd029..d8e60e69 100644 Binary files a/_module/ncs/mail_mb_setup.ncs and b/_module/ncs/mail_mb_setup.ncs differ diff --git a/_module/ncs/mail_onconv.ncs b/_module/ncs/mail_onconv.ncs index 2d74ae1c..e4866e96 100644 Binary files a/_module/ncs/mail_onconv.ncs and b/_module/ncs/mail_onconv.ncs differ diff --git a/_module/ncs/mail_preview.ncs b/_module/ncs/mail_preview.ncs index 74c6974d..0f7ebc30 100644 Binary files a/_module/ncs/mail_preview.ncs and b/_module/ncs/mail_preview.ncs differ diff --git a/_module/ncs/mail_reply.ncs b/_module/ncs/mail_reply.ncs index 5d3e8395..ad5a248a 100644 Binary files a/_module/ncs/mail_reply.ncs and b/_module/ncs/mail_reply.ncs differ diff --git a/_module/ncs/mail_send.ncs b/_module/ncs/mail_send.ncs index fae35fe5..5551893b 100644 Binary files a/_module/ncs/mail_send.ncs and b/_module/ncs/mail_send.ncs differ diff --git a/_module/ncs/mail_show01.ncs b/_module/ncs/mail_show01.ncs index ad51d85e..7469f25b 100644 Binary files a/_module/ncs/mail_show01.ncs and b/_module/ncs/mail_show01.ncs differ diff --git a/_module/ncs/mail_show02.ncs b/_module/ncs/mail_show02.ncs index 8a478dd4..16b74c39 100644 Binary files a/_module/ncs/mail_show02.ncs and b/_module/ncs/mail_show02.ncs differ diff --git a/_module/ncs/mail_show03.ncs b/_module/ncs/mail_show03.ncs index 4113b902..260ba661 100644 Binary files a/_module/ncs/mail_show03.ncs and b/_module/ncs/mail_show03.ncs differ diff --git a/_module/ncs/mail_show04.ncs b/_module/ncs/mail_show04.ncs index 08649c12..ba491997 100644 Binary files a/_module/ncs/mail_show04.ncs and b/_module/ncs/mail_show04.ncs differ diff --git a/_module/ncs/mail_show05.ncs b/_module/ncs/mail_show05.ncs index a2bdc758..61817421 100644 Binary files a/_module/ncs/mail_show05.ncs and b/_module/ncs/mail_show05.ncs differ diff --git a/_module/ncs/mail_show06.ncs b/_module/ncs/mail_show06.ncs index 22dd5842..b03dbab0 100644 Binary files a/_module/ncs/mail_show06.ncs and b/_module/ncs/mail_show06.ncs differ diff --git a/_module/ncs/mail_show07.ncs b/_module/ncs/mail_show07.ncs index c4491c43..d4f627b5 100644 Binary files a/_module/ncs/mail_show07.ncs and b/_module/ncs/mail_show07.ncs differ diff --git a/_module/ncs/mail_show08.ncs b/_module/ncs/mail_show08.ncs index d048645c..b5fe9607 100644 Binary files a/_module/ncs/mail_show08.ncs and b/_module/ncs/mail_show08.ncs differ diff --git a/_module/ncs/mail_show09.ncs b/_module/ncs/mail_show09.ncs index c9e384f7..1e104dc5 100644 Binary files a/_module/ncs/mail_show09.ncs and b/_module/ncs/mail_show09.ncs differ diff --git a/_module/ncs/mail_show10.ncs b/_module/ncs/mail_show10.ncs index 436f1198..f6c95ec8 100644 Binary files a/_module/ncs/mail_show10.ncs and b/_module/ncs/mail_show10.ncs differ diff --git a/_module/ncs/mail_showmessage.ncs b/_module/ncs/mail_showmessage.ncs index e1852190..11953ad1 100644 Binary files a/_module/ncs/mail_showmessage.ncs and b/_module/ncs/mail_showmessage.ncs differ diff --git a/_module/ncs/mail_subject.ncs b/_module/ncs/mail_subject.ncs index 5c30b684..6cdfd7c5 100644 Binary files a/_module/ncs/mail_subject.ncs and b/_module/ncs/mail_subject.ncs differ diff --git a/_module/ncs/merchant_arm_5k.ncs b/_module/ncs/merchant_arm_5k.ncs index 81795dfc..7689a596 100644 Binary files a/_module/ncs/merchant_arm_5k.ncs and b/_module/ncs/merchant_arm_5k.ncs differ diff --git a/_module/ncs/merchant_bow_5k.ncs b/_module/ncs/merchant_bow_5k.ncs index 83a6df6d..f72044c2 100644 Binary files a/_module/ncs/merchant_bow_5k.ncs and b/_module/ncs/merchant_bow_5k.ncs differ diff --git a/_module/ncs/merchant_gen_5k.ncs b/_module/ncs/merchant_gen_5k.ncs index 2f4eb39a..a5411447 100644 Binary files a/_module/ncs/merchant_gen_5k.ncs and b/_module/ncs/merchant_gen_5k.ncs differ diff --git a/_module/ncs/merchant_mag_5k.ncs b/_module/ncs/merchant_mag_5k.ncs index 2c93b1fa..f0f81b0e 100644 Binary files a/_module/ncs/merchant_mag_5k.ncs and b/_module/ncs/merchant_mag_5k.ncs differ diff --git a/_module/ncs/merchant_reset5k.ncs b/_module/ncs/merchant_reset5k.ncs index 5d7162dc..5ed55529 100644 Binary files a/_module/ncs/merchant_reset5k.ncs and b/_module/ncs/merchant_reset5k.ncs differ diff --git a/_module/ncs/merchant_reset_a.ncs b/_module/ncs/merchant_reset_a.ncs index 703f9930..ed7d718b 100644 Binary files a/_module/ncs/merchant_reset_a.ncs and b/_module/ncs/merchant_reset_a.ncs differ diff --git a/_module/ncs/merchant_scr2k.ncs b/_module/ncs/merchant_scr2k.ncs index 0beab7c8..fce981b9 100644 Binary files a/_module/ncs/merchant_scr2k.ncs and b/_module/ncs/merchant_scr2k.ncs differ diff --git a/_module/ncs/merchant_script.ncs b/_module/ncs/merchant_script.ncs index 8dfc5fb3..c2e773fb 100644 Binary files a/_module/ncs/merchant_script.ncs and b/_module/ncs/merchant_script.ncs differ diff --git a/_module/ncs/merchant_thief2k.ncs b/_module/ncs/merchant_thief2k.ncs index 242f622b..7201d5e5 100644 Binary files a/_module/ncs/merchant_thief2k.ncs and b/_module/ncs/merchant_thief2k.ncs differ diff --git a/_module/ncs/merm_ckvar1.ncs b/_module/ncs/merm_ckvar1.ncs index 4010f9a5..e4d75cba 100644 Binary files a/_module/ncs/merm_ckvar1.ncs and b/_module/ncs/merm_ckvar1.ncs differ diff --git a/_module/ncs/millerwife_done.ncs b/_module/ncs/millerwife_done.ncs index dbff63e4..05bd8002 100644 Binary files a/_module/ncs/millerwife_done.ncs and b/_module/ncs/millerwife_done.ncs differ diff --git a/_module/ncs/millwife_setvar.ncs b/_module/ncs/millwife_setvar.ncs index 6f2d76f7..999a6c22 100644 Binary files a/_module/ncs/millwife_setvar.ncs and b/_module/ncs/millwife_setvar.ncs differ diff --git a/_module/ncs/millwife_spoken2.ncs b/_module/ncs/millwife_spoken2.ncs index 221f83a9..7efbd374 100644 Binary files a/_module/ncs/millwife_spoken2.ncs and b/_module/ncs/millwife_spoken2.ncs differ diff --git a/_module/ncs/mm_prc_spells.ncs b/_module/ncs/mm_prc_spells.ncs new file mode 100644 index 00000000..97d42c9d Binary files /dev/null and b/_module/ncs/mm_prc_spells.ncs differ diff --git a/_module/ncs/mod_rest.ncs b/_module/ncs/mod_rest.ncs index b76b1c06..80c69919 100644 Binary files a/_module/ncs/mod_rest.ncs and b/_module/ncs/mod_rest.ncs differ diff --git a/_module/ncs/money_remove.ncs b/_module/ncs/money_remove.ncs index 99c9ecef..44b6b894 100644 Binary files a/_module/ncs/money_remove.ncs and b/_module/ncs/money_remove.ncs differ diff --git a/_module/ncs/monk_shop_ck.ncs b/_module/ncs/monk_shop_ck.ncs index e66fd2a0..c4d9bd95 100644 Binary files a/_module/ncs/monk_shop_ck.ncs and b/_module/ncs/monk_shop_ck.ncs differ diff --git a/_module/ncs/move_hcave_b.ncs b/_module/ncs/move_hcave_b.ncs index 05bdd390..58f1820b 100644 Binary files a/_module/ncs/move_hcave_b.ncs and b/_module/ncs/move_hcave_b.ncs differ diff --git a/_module/ncs/mud_golem_ondam.ncs b/_module/ncs/mud_golem_ondam.ncs index c47660fd..a0881675 100644 Binary files a/_module/ncs/mud_golem_ondam.ncs and b/_module/ncs/mud_golem_ondam.ncs differ diff --git a/_module/ncs/multiguild.ncs b/_module/ncs/multiguild.ncs index 4d3dc415..2cb8d142 100644 Binary files a/_module/ncs/multiguild.ncs and b/_module/ncs/multiguild.ncs differ diff --git a/_module/ncs/multiple_mob_att.ncs b/_module/ncs/multiple_mob_att.ncs index 57333b17..40b8f917 100644 Binary files a/_module/ncs/multiple_mob_att.ncs and b/_module/ncs/multiple_mob_att.ncs differ diff --git a/_module/ncs/nasbind1.ncs b/_module/ncs/nasbind1.ncs index 043bc5f8..dfc33708 100644 Binary files a/_module/ncs/nasbind1.ncs and b/_module/ncs/nasbind1.ncs differ diff --git a/_module/ncs/nasbind2.ncs b/_module/ncs/nasbind2.ncs index 22138f07..d6b9f748 100644 Binary files a/_module/ncs/nasbind2.ncs and b/_module/ncs/nasbind2.ncs differ diff --git a/_module/ncs/nc_innkeep_give.ncs b/_module/ncs/nc_innkeep_give.ncs index 809fae31..915da58f 100644 Binary files a/_module/ncs/nc_innkeep_give.ncs and b/_module/ncs/nc_innkeep_give.ncs differ diff --git a/_module/ncs/no_ai_atk.ncs b/_module/ncs/no_ai_atk.ncs index 6b23ec55..b8d5c373 100644 Binary files a/_module/ncs/no_ai_atk.ncs and b/_module/ncs/no_ai_atk.ncs differ diff --git a/_module/ncs/no_ai_blk.ncs b/_module/ncs/no_ai_blk.ncs index b7806759..40fcfb46 100644 Binary files a/_module/ncs/no_ai_blk.ncs and b/_module/ncs/no_ai_blk.ncs differ diff --git a/_module/ncs/no_ai_cmb.ncs b/_module/ncs/no_ai_cmb.ncs index 9cb8fd69..248a563e 100644 Binary files a/_module/ncs/no_ai_cmb.ncs and b/_module/ncs/no_ai_cmb.ncs differ diff --git a/_module/ncs/no_ai_cnv.ncs b/_module/ncs/no_ai_cnv.ncs index 0dcfad1f..8fdb892a 100644 Binary files a/_module/ncs/no_ai_cnv.ncs and b/_module/ncs/no_ai_cnv.ncs differ diff --git a/_module/ncs/no_ai_dam.ncs b/_module/ncs/no_ai_dam.ncs index 2477bc97..7689955f 100644 Binary files a/_module/ncs/no_ai_dam.ncs and b/_module/ncs/no_ai_dam.ncs differ diff --git a/_module/ncs/no_ai_dis.ncs b/_module/ncs/no_ai_dis.ncs index 8c5be4cd..0b32af12 100644 Binary files a/_module/ncs/no_ai_dis.ncs and b/_module/ncs/no_ai_dis.ncs differ diff --git a/_module/ncs/no_ai_dth.ncs b/_module/ncs/no_ai_dth.ncs index 40d76e33..8d665f04 100644 Binary files a/_module/ncs/no_ai_dth.ncs and b/_module/ncs/no_ai_dth.ncs differ diff --git a/_module/ncs/no_ai_hrt.ncs b/_module/ncs/no_ai_hrt.ncs index 0626e8ed..de658fa5 100644 Binary files a/_module/ncs/no_ai_hrt.ncs and b/_module/ncs/no_ai_hrt.ncs differ diff --git a/_module/ncs/no_ai_per.ncs b/_module/ncs/no_ai_per.ncs index db033faa..cf762372 100644 Binary files a/_module/ncs/no_ai_per.ncs and b/_module/ncs/no_ai_per.ncs differ diff --git a/_module/ncs/no_ai_spt.ncs b/_module/ncs/no_ai_spt.ncs index fdea4b5e..aadb3de9 100644 Binary files a/_module/ncs/no_ai_spt.ncs and b/_module/ncs/no_ai_spt.ncs differ diff --git a/_module/ncs/no_scr_cleanvars.ncs b/_module/ncs/no_scr_cleanvars.ncs index a1f44bdc..54858e14 100644 Binary files a/_module/ncs/no_scr_cleanvars.ncs and b/_module/ncs/no_scr_cleanvars.ncs differ diff --git a/_module/ncs/no_scr_excorpse.ncs b/_module/ncs/no_scr_excorpse.ncs index ccc1402c..fa7dd439 100644 Binary files a/_module/ncs/no_scr_excorpse.ncs and b/_module/ncs/no_scr_excorpse.ncs differ diff --git a/_module/ncs/no_scr_logeq.ncs b/_module/ncs/no_scr_logeq.ncs index f0289591..af6e18b2 100644 Binary files a/_module/ncs/no_scr_logeq.ncs and b/_module/ncs/no_scr_logeq.ncs differ diff --git a/_module/ncs/no_scr_shutdown.ncs b/_module/ncs/no_scr_shutdown.ncs index 0952e33b..f3f3bf86 100644 Binary files a/_module/ncs/no_scr_shutdown.ncs and b/_module/ncs/no_scr_shutdown.ncs differ diff --git a/_module/ncs/no_spn_beh.ncs b/_module/ncs/no_spn_beh.ncs index ec758024..8781d472 100644 Binary files a/_module/ncs/no_spn_beh.ncs and b/_module/ncs/no_spn_beh.ncs differ diff --git a/_module/ncs/no_spn_clr.ncs b/_module/ncs/no_spn_clr.ncs index b29f85d6..5405b084 100644 Binary files a/_module/ncs/no_spn_clr.ncs and b/_module/ncs/no_spn_clr.ncs differ diff --git a/_module/ncs/no_spn_fiend.ncs b/_module/ncs/no_spn_fiend.ncs index a361c18a..71d690ea 100644 Binary files a/_module/ncs/no_spn_fiend.ncs and b/_module/ncs/no_spn_fiend.ncs differ diff --git a/_module/ncs/no_spn_ftr.ncs b/_module/ncs/no_spn_ftr.ncs index c42716bb..815f872a 100644 Binary files a/_module/ncs/no_spn_ftr.ncs and b/_module/ncs/no_spn_ftr.ncs differ diff --git a/_module/ncs/no_spn_ftrfire.ncs b/_module/ncs/no_spn_ftrfire.ncs index 302b306e..0823ac04 100644 Binary files a/_module/ncs/no_spn_ftrfire.ncs and b/_module/ncs/no_spn_ftrfire.ncs differ diff --git a/_module/ncs/no_spn_ftrsize.ncs b/_module/ncs/no_spn_ftrsize.ncs index 302b306e..0823ac04 100644 Binary files a/_module/ncs/no_spn_ftrsize.ncs and b/_module/ncs/no_spn_ftrsize.ncs differ diff --git a/_module/ncs/no_spn_ftrwater.ncs b/_module/ncs/no_spn_ftrwater.ncs index e7f93be2..0f3ce6fe 100644 Binary files a/_module/ncs/no_spn_ftrwater.ncs and b/_module/ncs/no_spn_ftrwater.ncs differ diff --git a/_module/ncs/no_spn_ftrwood.ncs b/_module/ncs/no_spn_ftrwood.ncs index 52c174d7..521f8896 100644 Binary files a/_module/ncs/no_spn_ftrwood.ncs and b/_module/ncs/no_spn_ftrwood.ncs differ diff --git a/_module/ncs/no_spn_melcast1.ncs b/_module/ncs/no_spn_melcast1.ncs index 913ad885..50fde0a8 100644 Binary files a/_module/ncs/no_spn_melcast1.ncs and b/_module/ncs/no_spn_melcast1.ncs differ diff --git a/_module/ncs/no_spn_melcast2.ncs b/_module/ncs/no_spn_melcast2.ncs index 85330f11..0f12cfba 100644 Binary files a/_module/ncs/no_spn_melcast2.ncs and b/_module/ncs/no_spn_melcast2.ncs differ diff --git a/_module/ncs/no_spn_melcast3.ncs b/_module/ncs/no_spn_melcast3.ncs index 21839deb..f56f1f43 100644 Binary files a/_module/ncs/no_spn_melcast3.ncs and b/_module/ncs/no_spn_melcast3.ncs differ diff --git a/_module/ncs/no_spn_melenh.ncs b/_module/ncs/no_spn_melenh.ncs index 300dd07d..d05ef7e3 100644 Binary files a/_module/ncs/no_spn_melenh.ncs and b/_module/ncs/no_spn_melenh.ncs differ diff --git a/_module/ncs/no_spn_rftr.ncs b/_module/ncs/no_spn_rftr.ncs index 17b1e469..50892a6a 100644 Binary files a/_module/ncs/no_spn_rftr.ncs and b/_module/ncs/no_spn_rftr.ncs differ diff --git a/_module/ncs/no_spn_rftrcast1.ncs b/_module/ncs/no_spn_rftrcast1.ncs index 093ce930..49d6eb34 100644 Binary files a/_module/ncs/no_spn_rftrcast1.ncs and b/_module/ncs/no_spn_rftrcast1.ncs differ diff --git a/_module/ncs/no_spn_rngcast3.ncs b/_module/ncs/no_spn_rngcast3.ncs index ad6bfa20..596a9c9c 100644 Binary files a/_module/ncs/no_spn_rngcast3.ncs and b/_module/ncs/no_spn_rngcast3.ncs differ diff --git a/_module/ncs/no_spn_rngt1comm.ncs b/_module/ncs/no_spn_rngt1comm.ncs index ea3de74f..d97aeb77 100644 Binary files a/_module/ncs/no_spn_rngt1comm.ncs and b/_module/ncs/no_spn_rngt1comm.ncs differ diff --git a/_module/ncs/no_spn_rngt2comm.ncs b/_module/ncs/no_spn_rngt2comm.ncs index 42f76772..ae260ab0 100644 Binary files a/_module/ncs/no_spn_rngt2comm.ncs and b/_module/ncs/no_spn_rngt2comm.ncs differ diff --git a/_module/ncs/no_spn_rngt3comm.ncs b/_module/ncs/no_spn_rngt3comm.ncs index 4950a487..2c45b35f 100644 Binary files a/_module/ncs/no_spn_rngt3comm.ncs and b/_module/ncs/no_spn_rngt3comm.ncs differ diff --git a/_module/ncs/no_spn_rog.ncs b/_module/ncs/no_spn_rog.ncs index c3631625..f61ca2e6 100644 Binary files a/_module/ncs/no_spn_rog.ncs and b/_module/ncs/no_spn_rog.ncs differ diff --git a/_module/ncs/no_spn_rogcast1.ncs b/_module/ncs/no_spn_rogcast1.ncs index 22037a35..5add8ace 100644 Binary files a/_module/ncs/no_spn_rogcast1.ncs and b/_module/ncs/no_spn_rogcast1.ncs differ diff --git a/_module/ncs/no_spn_rogcast3.ncs b/_module/ncs/no_spn_rogcast3.ncs index bdbb1fc3..1ad31d08 100644 Binary files a/_module/ncs/no_spn_rogcast3.ncs and b/_module/ncs/no_spn_rogcast3.ncs differ diff --git a/_module/ncs/no_spn_rogenh.ncs b/_module/ncs/no_spn_rogenh.ncs index 4927a0b7..9f05a933 100644 Binary files a/_module/ncs/no_spn_rogenh.ncs and b/_module/ncs/no_spn_rogenh.ncs differ diff --git a/_module/ncs/no_spn_rogtel.ncs b/_module/ncs/no_spn_rogtel.ncs index 1c92b0c9..f02f4285 100644 Binary files a/_module/ncs/no_spn_rogtel.ncs and b/_module/ncs/no_spn_rogtel.ncs differ diff --git a/_module/ncs/no_spn_summ.ncs b/_module/ncs/no_spn_summ.ncs index 89f312d8..d19c4073 100644 Binary files a/_module/ncs/no_spn_summ.ncs and b/_module/ncs/no_spn_summ.ncs differ diff --git a/_module/ncs/no_spn_wiz.ncs b/_module/ncs/no_spn_wiz.ncs index 051ef69a..0d277262 100644 Binary files a/_module/ncs/no_spn_wiz.ncs and b/_module/ncs/no_spn_wiz.ncs differ diff --git a/_module/ncs/npc2npcconvo.ncs b/_module/ncs/npc2npcconvo.ncs index 4815baec..82dca3d4 100644 Binary files a/_module/ncs/npc2npcconvo.ncs and b/_module/ncs/npc2npcconvo.ncs differ diff --git a/_module/ncs/npc_rndwayp_go.ncs b/_module/ncs/npc_rndwayp_go.ncs index c482ece9..f18e9e86 100644 Binary files a/_module/ncs/npc_rndwayp_go.ncs and b/_module/ncs/npc_rndwayp_go.ncs differ diff --git a/_module/ncs/npc_see_item.ncs b/_module/ncs/npc_see_item.ncs index 71bcc646..96118b9a 100644 Binary files a/_module/ncs/npc_see_item.ncs and b/_module/ncs/npc_see_item.ncs differ diff --git a/_module/ncs/npc_sit_chair.ncs b/_module/ncs/npc_sit_chair.ncs index 5f0261fc..af3e671e 100644 Binary files a/_module/ncs/npc_sit_chair.ncs and b/_module/ncs/npc_sit_chair.ncs differ diff --git a/_module/ncs/npc_sit_stay.ncs b/_module/ncs/npc_sit_stay.ncs index 46931129..1d813bba 100644 Binary files a/_module/ncs/npc_sit_stay.ncs and b/_module/ncs/npc_sit_stay.ncs differ diff --git a/_module/ncs/npc_sit_woodenbe.ncs b/_module/ncs/npc_sit_woodenbe.ncs index 46be7940..a60a533c 100644 Binary files a/_module/ncs/npc_sit_woodenbe.ncs and b/_module/ncs/npc_sit_woodenbe.ncs differ diff --git a/_module/ncs/nw_c2_9icewraith.ncs b/_module/ncs/nw_c2_9icewraith.ncs index 37c04053..cc84172d 100644 Binary files a/_module/ncs/nw_c2_9icewraith.ncs and b/_module/ncs/nw_c2_9icewraith.ncs differ diff --git a/_module/ncs/nw_c2_9itigwrait.ncs b/_module/ncs/nw_c2_9itigwrait.ncs index 27f46074..847fe0e3 100644 Binary files a/_module/ncs/nw_c2_9itigwrait.ncs and b/_module/ncs/nw_c2_9itigwrait.ncs differ diff --git a/_module/ncs/nw_c2_bossdie.ncs b/_module/ncs/nw_c2_bossdie.ncs index ec5a325a..f5523d6c 100644 Binary files a/_module/ncs/nw_c2_bossdie.ncs and b/_module/ncs/nw_c2_bossdie.ncs differ diff --git a/_module/ncs/nw_c2_bossspawn.ncs b/_module/ncs/nw_c2_bossspawn.ncs index 7330f43b..dd738bd1 100644 Binary files a/_module/ncs/nw_c2_bossspawn.ncs and b/_module/ncs/nw_c2_bossspawn.ncs differ diff --git a/_module/ncs/nw_c2_def9nomove.ncs b/_module/ncs/nw_c2_def9nomove.ncs index 11e006b4..76a3933e 100644 Binary files a/_module/ncs/nw_c2_def9nomove.ncs and b/_module/ncs/nw_c2_def9nomove.ncs differ diff --git a/_module/ncs/nw_c2_defaul1b.ncs b/_module/ncs/nw_c2_defaul1b.ncs index 2c020817..42b684ad 100644 Binary files a/_module/ncs/nw_c2_defaul1b.ncs and b/_module/ncs/nw_c2_defaul1b.ncs differ diff --git a/_module/ncs/nw_c2_defaul4.ncs b/_module/ncs/nw_c2_defaul4.ncs index 46609693..1e8ea573 100644 Binary files a/_module/ncs/nw_c2_defaul4.ncs and b/_module/ncs/nw_c2_defaul4.ncs differ diff --git a/_module/ncs/nw_c2_default1.ncs b/_module/ncs/nw_c2_default1.ncs index 67a688a8..b2439bfc 100644 Binary files a/_module/ncs/nw_c2_default1.ncs and b/_module/ncs/nw_c2_default1.ncs differ diff --git a/_module/ncs/nw_c2_default2.ncs b/_module/ncs/nw_c2_default2.ncs index a176fe94..cebb6f20 100644 Binary files a/_module/ncs/nw_c2_default2.ncs and b/_module/ncs/nw_c2_default2.ncs differ diff --git a/_module/ncs/nw_c2_default3.ncs b/_module/ncs/nw_c2_default3.ncs index bad74a1a..36e8755b 100644 Binary files a/_module/ncs/nw_c2_default3.ncs and b/_module/ncs/nw_c2_default3.ncs differ diff --git a/_module/ncs/nw_c2_default4.ncs b/_module/ncs/nw_c2_default4.ncs index 0bf5794b..e924bf00 100644 Binary files a/_module/ncs/nw_c2_default4.ncs and b/_module/ncs/nw_c2_default4.ncs differ diff --git a/_module/ncs/nw_c2_default5.ncs b/_module/ncs/nw_c2_default5.ncs index bae829d4..21d30294 100644 Binary files a/_module/ncs/nw_c2_default5.ncs and b/_module/ncs/nw_c2_default5.ncs differ diff --git a/_module/ncs/nw_c2_default6.ncs b/_module/ncs/nw_c2_default6.ncs index a7d487f3..925dccde 100644 Binary files a/_module/ncs/nw_c2_default6.ncs and b/_module/ncs/nw_c2_default6.ncs differ diff --git a/_module/ncs/nw_c2_default7_g.ncs b/_module/ncs/nw_c2_default7_g.ncs index cd156c5d..80bf6d6c 100644 Binary files a/_module/ncs/nw_c2_default7_g.ncs and b/_module/ncs/nw_c2_default7_g.ncs differ diff --git a/_module/ncs/nw_c2_default8.ncs b/_module/ncs/nw_c2_default8.ncs index 57914022..af3c96cc 100644 Binary files a/_module/ncs/nw_c2_default8.ncs and b/_module/ncs/nw_c2_default8.ncs differ diff --git a/_module/ncs/nw_c2_default9_a.ncs b/_module/ncs/nw_c2_default9_a.ncs index 244b9dd4..b8c9b841 100644 Binary files a/_module/ncs/nw_c2_default9_a.ncs and b/_module/ncs/nw_c2_default9_a.ncs differ diff --git a/_module/ncs/nw_c2_default9_r.ncs b/_module/ncs/nw_c2_default9_r.ncs index c58a3490..3388d2d2 100644 Binary files a/_module/ncs/nw_c2_default9_r.ncs and b/_module/ncs/nw_c2_default9_r.ncs differ diff --git a/_module/ncs/nw_c2_default9_t.ncs b/_module/ncs/nw_c2_default9_t.ncs index 276b6786..b612c4ff 100644 Binary files a/_module/ncs/nw_c2_default9_t.ncs and b/_module/ncs/nw_c2_default9_t.ncs differ diff --git a/_module/ncs/nw_c2_default9gh.ncs b/_module/ncs/nw_c2_default9gh.ncs index b3ae46a1..e61a62de 100644 Binary files a/_module/ncs/nw_c2_default9gh.ncs and b/_module/ncs/nw_c2_default9gh.ncs differ diff --git a/_module/ncs/nw_c2_default9pr.ncs b/_module/ncs/nw_c2_default9pr.ncs index 33f66517..353c8e9c 100644 Binary files a/_module/ncs/nw_c2_default9pr.ncs and b/_module/ncs/nw_c2_default9pr.ncs differ diff --git a/_module/ncs/nw_c2_default9si.ncs b/_module/ncs/nw_c2_default9si.ncs index d835df3c..bb72cf81 100644 Binary files a/_module/ncs/nw_c2_default9si.ncs and b/_module/ncs/nw_c2_default9si.ncs differ diff --git a/_module/ncs/nw_c2_defaultb.ncs b/_module/ncs/nw_c2_defaultb.ncs index b59e4c1f..ca34e09b 100644 Binary files a/_module/ncs/nw_c2_defaultb.ncs and b/_module/ncs/nw_c2_defaultb.ncs differ diff --git a/_module/ncs/nw_c2_defaulte.ncs b/_module/ncs/nw_c2_defaulte.ncs index 1eb98734..5876b028 100644 Binary files a/_module/ncs/nw_c2_defaulte.ncs and b/_module/ncs/nw_c2_defaulte.ncs differ diff --git a/_module/ncs/nw_c2_dimdoor.ncs b/_module/ncs/nw_c2_dimdoor.ncs index 8e32d3de..f85c51d3 100644 Binary files a/_module/ncs/nw_c2_dimdoor.ncs and b/_module/ncs/nw_c2_dimdoor.ncs differ diff --git a/_module/ncs/nw_c2_dimdoors.ncs b/_module/ncs/nw_c2_dimdoors.ncs index a240552e..1bc1b2cc 100644 Binary files a/_module/ncs/nw_c2_dimdoors.ncs and b/_module/ncs/nw_c2_dimdoors.ncs differ diff --git a/_module/ncs/nw_c2_fastbuff9.ncs b/_module/ncs/nw_c2_fastbuff9.ncs index bbeccfbe..d4cb570c 100644 Binary files a/_module/ncs/nw_c2_fastbuff9.ncs and b/_module/ncs/nw_c2_fastbuff9.ncs differ diff --git a/_module/ncs/nw_c2_herbivore.ncs b/_module/ncs/nw_c2_herbivore.ncs index 3759124c..478d0c73 100644 Binary files a/_module/ncs/nw_c2_herbivore.ncs and b/_module/ncs/nw_c2_herbivore.ncs differ diff --git a/_module/ncs/nw_c2_herbivore2.ncs b/_module/ncs/nw_c2_herbivore2.ncs index 3759124c..478d0c73 100644 Binary files a/_module/ncs/nw_c2_herbivore2.ncs and b/_module/ncs/nw_c2_herbivore2.ncs differ diff --git a/_module/ncs/nw_c2_highai.ncs b/_module/ncs/nw_c2_highai.ncs index e2557f38..af540c34 100644 Binary files a/_module/ncs/nw_c2_highai.ncs and b/_module/ncs/nw_c2_highai.ncs differ diff --git a/_module/ncs/nw_c2_shadow9.ncs b/_module/ncs/nw_c2_shadow9.ncs index b52689cd..cf0a36bf 100644 Binary files a/_module/ncs/nw_c2_shadow9.ncs and b/_module/ncs/nw_c2_shadow9.ncs differ diff --git a/_module/ncs/nw_c2_water9.ncs b/_module/ncs/nw_c2_water9.ncs index d05464f1..fb626855 100644 Binary files a/_module/ncs/nw_c2_water9.ncs and b/_module/ncs/nw_c2_water9.ncs differ diff --git a/_module/ncs/nw_c2_water9b.ncs b/_module/ncs/nw_c2_water9b.ncs index 7d0e958e..1967fc91 100644 Binary files a/_module/ncs/nw_c2_water9b.ncs and b/_module/ncs/nw_c2_water9b.ncs differ diff --git a/_module/ncs/nw_ch_ac1.ncs b/_module/ncs/nw_ch_ac1.ncs new file mode 100644 index 00000000..ba260b23 Binary files /dev/null and b/_module/ncs/nw_ch_ac1.ncs differ diff --git a/_module/ncs/nw_ch_ac2.ncs b/_module/ncs/nw_ch_ac2.ncs new file mode 100644 index 00000000..e02c855b Binary files /dev/null and b/_module/ncs/nw_ch_ac2.ncs differ diff --git a/_module/ncs/nw_ch_ac3.ncs b/_module/ncs/nw_ch_ac3.ncs new file mode 100644 index 00000000..aa9d108e Binary files /dev/null and b/_module/ncs/nw_ch_ac3.ncs differ diff --git a/_module/ncs/nw_ch_ac4.ncs b/_module/ncs/nw_ch_ac4.ncs new file mode 100644 index 00000000..0edc03ce Binary files /dev/null and b/_module/ncs/nw_ch_ac4.ncs differ diff --git a/_module/ncs/nw_ch_ac5.ncs b/_module/ncs/nw_ch_ac5.ncs new file mode 100644 index 00000000..df1e19c5 Binary files /dev/null and b/_module/ncs/nw_ch_ac5.ncs differ diff --git a/_module/ncs/nw_ch_ac6.ncs b/_module/ncs/nw_ch_ac6.ncs new file mode 100644 index 00000000..ce42943e Binary files /dev/null and b/_module/ncs/nw_ch_ac6.ncs differ diff --git a/_module/ncs/nw_ch_ac8.ncs b/_module/ncs/nw_ch_ac8.ncs new file mode 100644 index 00000000..1f8f4677 Binary files /dev/null and b/_module/ncs/nw_ch_ac8.ncs differ diff --git a/_module/ncs/nw_ch_aca.ncs b/_module/ncs/nw_ch_aca.ncs new file mode 100644 index 00000000..9215c812 Binary files /dev/null and b/_module/ncs/nw_ch_aca.ncs differ diff --git a/_module/ncs/nw_ch_acb.ncs b/_module/ncs/nw_ch_acb.ncs new file mode 100644 index 00000000..ff643f75 Binary files /dev/null and b/_module/ncs/nw_ch_acb.ncs differ diff --git a/_module/ncs/nw_ch_ace.ncs b/_module/ncs/nw_ch_ace.ncs new file mode 100644 index 00000000..04b458a1 Binary files /dev/null and b/_module/ncs/nw_ch_ace.ncs differ diff --git a/_module/ncs/nw_ch_heal_75.ncs b/_module/ncs/nw_ch_heal_75.ncs index 47431533..0de1bd08 100644 Binary files a/_module/ncs/nw_ch_heal_75.ncs and b/_module/ncs/nw_ch_heal_75.ncs differ diff --git a/_module/ncs/nw_ch_summon_9.ncs b/_module/ncs/nw_ch_summon_9.ncs new file mode 100644 index 00000000..3f2a89ca Binary files /dev/null and b/_module/ncs/nw_ch_summon_9.ncs differ diff --git a/_module/ncs/nw_o2_boss.ncs b/_module/ncs/nw_o2_boss.ncs index 57cdce63..ff528bfd 100644 Binary files a/_module/ncs/nw_o2_boss.ncs and b/_module/ncs/nw_o2_boss.ncs differ diff --git a/_module/ncs/nw_o2_classweap.ncs b/_module/ncs/nw_o2_classweap.ncs index 354ca56c..100d8c1f 100644 Binary files a/_module/ncs/nw_o2_classweap.ncs and b/_module/ncs/nw_o2_classweap.ncs differ diff --git a/_module/ncs/nw_o2_generalhig.ncs b/_module/ncs/nw_o2_generalhig.ncs index 35297075..5bbe7343 100644 Binary files a/_module/ncs/nw_o2_generalhig.ncs and b/_module/ncs/nw_o2_generalhig.ncs differ diff --git a/_module/ncs/nw_o2_generallow.ncs b/_module/ncs/nw_o2_generallow.ncs index 55674bc1..72b50dc8 100644 Binary files a/_module/ncs/nw_o2_generallow.ncs and b/_module/ncs/nw_o2_generallow.ncs differ diff --git a/_module/ncs/nw_o2_generalmed.ncs b/_module/ncs/nw_o2_generalmed.ncs index 169bf904..eb2b6582 100644 Binary files a/_module/ncs/nw_o2_generalmed.ncs and b/_module/ncs/nw_o2_generalmed.ncs differ diff --git a/_module/ncs/nw_undyingspawn.ncs b/_module/ncs/nw_undyingspawn.ncs index 678dc12e..1dd98c5f 100644 Binary files a/_module/ncs/nw_undyingspawn.ncs and b/_module/ncs/nw_undyingspawn.ncs differ diff --git a/_module/ncs/ogre_toll1.ncs b/_module/ncs/ogre_toll1.ncs index 9448d815..9d6b2461 100644 Binary files a/_module/ncs/ogre_toll1.ncs and b/_module/ncs/ogre_toll1.ncs differ diff --git a/_module/ncs/ogre_toll2.ncs b/_module/ncs/ogre_toll2.ncs index b154c53c..72471c6b 100644 Binary files a/_module/ncs/ogre_toll2.ncs and b/_module/ncs/ogre_toll2.ncs differ diff --git a/_module/ncs/ohs_keygen.ncs b/_module/ncs/ohs_keygen.ncs index af2fe20a..fc8a7b2d 100644 Binary files a/_module/ncs/ohs_keygen.ncs and b/_module/ncs/ohs_keygen.ncs differ diff --git a/_module/ncs/ohswhtkeygen.ncs b/_module/ncs/ohswhtkeygen.ncs index b09ae28a..46b0fe8b 100644 Binary files a/_module/ncs/ohswhtkeygen.ncs and b/_module/ncs/ohswhtkeygen.ncs differ diff --git a/_module/ncs/oldman_sit.ncs b/_module/ncs/oldman_sit.ncs index 2d80a366..af82262e 100644 Binary files a/_module/ncs/oldman_sit.ncs and b/_module/ncs/oldman_sit.ncs differ diff --git a/_module/ncs/olina_ball_spawn.ncs b/_module/ncs/olina_ball_spawn.ncs index 22c9b080..dae29a21 100644 Binary files a/_module/ncs/olina_ball_spawn.ncs and b/_module/ncs/olina_ball_spawn.ncs differ diff --git a/_module/ncs/olina_ck_ball.ncs b/_module/ncs/olina_ck_ball.ncs index b5702d33..8f9c6d4e 100644 Binary files a/_module/ncs/olina_ck_ball.ncs and b/_module/ncs/olina_ck_ball.ncs differ diff --git a/_module/ncs/olina_end.ncs b/_module/ncs/olina_end.ncs index fc96bafe..b8b64bfc 100644 Binary files a/_module/ncs/olina_end.ncs and b/_module/ncs/olina_end.ncs differ diff --git a/_module/ncs/olina_getvar1.ncs b/_module/ncs/olina_getvar1.ncs index ebb939a8..1c69c8ac 100644 Binary files a/_module/ncs/olina_getvar1.ncs and b/_module/ncs/olina_getvar1.ncs differ diff --git a/_module/ncs/olina_getvar2.ncs b/_module/ncs/olina_getvar2.ncs index 9ad1aadc..68804974 100644 Binary files a/_module/ncs/olina_getvar2.ncs and b/_module/ncs/olina_getvar2.ncs differ diff --git a/_module/ncs/olinaleave.ncs b/_module/ncs/olinaleave.ncs index 81982f50..75ef2f16 100644 Binary files a/_module/ncs/olinaleave.ncs and b/_module/ncs/olinaleave.ncs differ diff --git a/_module/ncs/on_damage_dr.ncs b/_module/ncs/on_damage_dr.ncs index 4b6ca8f4..58e1e584 100644 Binary files a/_module/ncs/on_damage_dr.ncs and b/_module/ncs/on_damage_dr.ncs differ diff --git a/_module/ncs/on_death_explode.ncs b/_module/ncs/on_death_explode.ncs index 9889d527..c83dbc16 100644 Binary files a/_module/ncs/on_death_explode.ncs and b/_module/ncs/on_death_explode.ncs differ diff --git a/_module/ncs/on_death_yin.ncs b/_module/ncs/on_death_yin.ncs index 0018088f..2a573df4 100644 Binary files a/_module/ncs/on_death_yin.ncs and b/_module/ncs/on_death_yin.ncs differ diff --git a/_module/ncs/on_deathyang.ncs b/_module/ncs/on_deathyang.ncs index 8bebb188..35ac27d4 100644 Binary files a/_module/ncs/on_deathyang.ncs and b/_module/ncs/on_deathyang.ncs differ diff --git a/_module/ncs/on_sit_bench_2.ncs b/_module/ncs/on_sit_bench_2.ncs index 7f8d2613..22e3c3e4 100644 Binary files a/_module/ncs/on_sit_bench_2.ncs and b/_module/ncs/on_sit_bench_2.ncs differ diff --git a/_module/ncs/on_spawn_wander.ncs b/_module/ncs/on_spawn_wander.ncs index d3a695f1..3ca5daf5 100644 Binary files a/_module/ncs/on_spawn_wander.ncs and b/_module/ncs/on_spawn_wander.ncs differ diff --git a/_module/ncs/onclose_air.ncs b/_module/ncs/onclose_air.ncs index 93cd352a..bf70f6c1 100644 Binary files a/_module/ncs/onclose_air.ncs and b/_module/ncs/onclose_air.ncs differ diff --git a/_module/ncs/onclose_earth.ncs b/_module/ncs/onclose_earth.ncs index 4d4252ad..85bd723c 100644 Binary files a/_module/ncs/onclose_earth.ncs and b/_module/ncs/onclose_earth.ncs differ diff --git a/_module/ncs/onclose_fire.ncs b/_module/ncs/onclose_fire.ncs index c1cf12d7..23c05076 100644 Binary files a/_module/ncs/onclose_fire.ncs and b/_module/ncs/onclose_fire.ncs differ diff --git a/_module/ncs/onclose_haven.ncs b/_module/ncs/onclose_haven.ncs index 65db0f91..26e6d976 100644 Binary files a/_module/ncs/onclose_haven.ncs and b/_module/ncs/onclose_haven.ncs differ diff --git a/_module/ncs/onclose_water.ncs b/_module/ncs/onclose_water.ncs index a731d920..4c385d9c 100644 Binary files a/_module/ncs/onclose_water.ncs and b/_module/ncs/onclose_water.ncs differ diff --git a/_module/ncs/ondeath_portal.ncs b/_module/ncs/ondeath_portal.ncs index 3ccaf5d7..11c2207e 100644 Binary files a/_module/ncs/ondeath_portal.ncs and b/_module/ncs/ondeath_portal.ncs differ diff --git a/_module/ncs/ondeath_thief.ncs b/_module/ncs/ondeath_thief.ncs index bba9c656..4779c4d7 100644 Binary files a/_module/ncs/ondeath_thief.ncs and b/_module/ncs/ondeath_thief.ncs differ diff --git a/_module/ncs/onent_beamoff.ncs b/_module/ncs/onent_beamoff.ncs index b70ea836..7f48287e 100644 Binary files a/_module/ncs/onent_beamoff.ncs and b/_module/ncs/onent_beamoff.ncs differ diff --git a/_module/ncs/onent_beamon.ncs b/_module/ncs/onent_beamon.ncs index 8fae239b..14cc59e8 100644 Binary files a/_module/ncs/onent_beamon.ncs and b/_module/ncs/onent_beamon.ncs differ diff --git a/_module/ncs/onent_beamzone.ncs b/_module/ncs/onent_beamzone.ncs index 17721d10..209af2aa 100644 Binary files a/_module/ncs/onent_beamzone.ncs and b/_module/ncs/onent_beamzone.ncs differ diff --git a/_module/ncs/onext_beamall.ncs b/_module/ncs/onext_beamall.ncs index a53991eb..38f26011 100644 Binary files a/_module/ncs/onext_beamall.ncs and b/_module/ncs/onext_beamall.ncs differ diff --git a/_module/ncs/onfailtoopen1.ncs b/_module/ncs/onfailtoopen1.ncs index 922217b5..654d7b1f 100644 Binary files a/_module/ncs/onfailtoopen1.ncs and b/_module/ncs/onfailtoopen1.ncs differ diff --git a/_module/ncs/onfailtoopen2.ncs b/_module/ncs/onfailtoopen2.ncs index fe34bdc6..b696273d 100644 Binary files a/_module/ncs/onfailtoopen2.ncs and b/_module/ncs/onfailtoopen2.ncs differ diff --git a/_module/ncs/onhb_beamsource.ncs b/_module/ncs/onhb_beamsource.ncs index 673e6066..8a17e447 100644 Binary files a/_module/ncs/onhb_beamsource.ncs and b/_module/ncs/onhb_beamsource.ncs differ diff --git a/_module/ncs/onuse_beamlever.ncs b/_module/ncs/onuse_beamlever.ncs index 0fb17d49..2ec1d759 100644 Binary files a/_module/ncs/onuse_beamlever.ncs and b/_module/ncs/onuse_beamlever.ncs differ diff --git a/_module/ncs/open_attack_pc.ncs b/_module/ncs/open_attack_pc.ncs index 4cb38864..c2bb284c 100644 Binary files a/_module/ncs/open_attack_pc.ncs and b/_module/ncs/open_attack_pc.ncs differ diff --git a/_module/ncs/open_tolldoor.ncs b/_module/ncs/open_tolldoor.ncs index 29cdc551..bd6804c4 100644 Binary files a/_module/ncs/open_tolldoor.ncs and b/_module/ncs/open_tolldoor.ncs differ diff --git a/_module/ncs/open_waterdoor.ncs b/_module/ncs/open_waterdoor.ncs index d9e9ac79..1c0aa3f0 100644 Binary files a/_module/ncs/open_waterdoor.ncs and b/_module/ncs/open_waterdoor.ncs differ diff --git a/_module/ncs/open_wetdoor.ncs b/_module/ncs/open_wetdoor.ncs index f32d9586..d9b14c6c 100644 Binary files a/_module/ncs/open_wetdoor.ncs and b/_module/ncs/open_wetdoor.ncs differ diff --git a/_module/ncs/opentombdoor1.ncs b/_module/ncs/opentombdoor1.ncs index c00904ac..6ab1ec9e 100644 Binary files a/_module/ncs/opentombdoor1.ncs and b/_module/ncs/opentombdoor1.ncs differ diff --git a/_module/ncs/opentombdoor2.ncs b/_module/ncs/opentombdoor2.ncs index aed6605a..c8ed833e 100644 Binary files a/_module/ncs/opentombdoor2.ncs and b/_module/ncs/opentombdoor2.ncs differ diff --git a/_module/ncs/openwolfdoors1.ncs b/_module/ncs/openwolfdoors1.ncs index 26af1218..a28fe5e7 100644 Binary files a/_module/ncs/openwolfdoors1.ncs and b/_module/ncs/openwolfdoors1.ncs differ diff --git a/_module/ncs/openwolfdoors2.ncs b/_module/ncs/openwolfdoors2.ncs index f452092c..8c9ebaec 100644 Binary files a/_module/ncs/openwolfdoors2.ncs and b/_module/ncs/openwolfdoors2.ncs differ diff --git a/_module/ncs/os_demonheadinn.ncs b/_module/ncs/os_demonheadinn.ncs index 4d2f44b1..51ed7937 100644 Binary files a/_module/ncs/os_demonheadinn.ncs and b/_module/ncs/os_demonheadinn.ncs differ diff --git a/_module/ncs/os_guild_gso.ncs b/_module/ncs/os_guild_gso.ncs index 9f1cecef..632b288e 100644 Binary files a/_module/ncs/os_guild_gso.ncs and b/_module/ncs/os_guild_gso.ncs differ diff --git a/_module/ncs/os_sago.ncs b/_module/ncs/os_sago.ncs index dc5fd8d7..722150f9 100644 Binary files a/_module/ncs/os_sago.ncs and b/_module/ncs/os_sago.ncs differ diff --git a/_module/ncs/os_skull_ball.ncs b/_module/ncs/os_skull_ball.ncs index 24b3191a..58f3eac4 100644 Binary files a/_module/ncs/os_skull_ball.ncs and b/_module/ncs/os_skull_ball.ncs differ diff --git a/_module/ncs/pac_haunt.ncs b/_module/ncs/pac_haunt.ncs index 22ddf45f..1f6c12a9 100644 Binary files a/_module/ncs/pac_haunt.ncs and b/_module/ncs/pac_haunt.ncs differ diff --git a/_module/ncs/pac_recreate.ncs b/_module/ncs/pac_recreate.ncs index 802d1a98..1ddeca1b 100644 Binary files a/_module/ncs/pac_recreate.ncs and b/_module/ncs/pac_recreate.ncs differ diff --git a/_module/ncs/pass_onspawn1.ncs b/_module/ncs/pass_onspawn1.ncs index ded885bb..03ff7144 100644 Binary files a/_module/ncs/pass_onspawn1.ncs and b/_module/ncs/pass_onspawn1.ncs differ diff --git a/_module/ncs/pass_onspawn2.ncs b/_module/ncs/pass_onspawn2.ncs index e2b9d1c8..e2c748b6 100644 Binary files a/_module/ncs/pass_onspawn2.ncs and b/_module/ncs/pass_onspawn2.ncs differ diff --git a/_module/ncs/pass_userdef1.ncs b/_module/ncs/pass_userdef1.ncs index c795de50..0bfd7e6f 100644 Binary files a/_module/ncs/pass_userdef1.ncs and b/_module/ncs/pass_userdef1.ncs differ diff --git a/_module/ncs/pass_userdef2.ncs b/_module/ncs/pass_userdef2.ncs index da8d9696..9113cf22 100644 Binary files a/_module/ncs/pass_userdef2.ncs and b/_module/ncs/pass_userdef2.ncs differ diff --git a/_module/ncs/patchnoattack.ncs b/_module/ncs/patchnoattack.ncs index 1d44025d..2c096aae 100644 Binary files a/_module/ncs/patchnoattack.ncs and b/_module/ncs/patchnoattack.ncs differ diff --git a/_module/ncs/pathoflightring.ncs b/_module/ncs/pathoflightring.ncs index 38c39e72..fac9371a 100644 Binary files a/_module/ncs/pathoflightring.ncs and b/_module/ncs/pathoflightring.ncs differ diff --git a/_module/ncs/pc_savebuffs.ncs b/_module/ncs/pc_savebuffs.ncs new file mode 100644 index 00000000..94413251 Binary files /dev/null and b/_module/ncs/pc_savebuffs.ncs differ diff --git a/_module/ncs/pe_buffing.ncs b/_module/ncs/pe_buffing.ncs new file mode 100644 index 00000000..3eaeaea6 Binary files /dev/null and b/_module/ncs/pe_buffing.ncs differ diff --git a/_module/ncs/pe_crafting.ncs b/_module/ncs/pe_crafting.ncs new file mode 100644 index 00000000..6f7cb01c Binary files /dev/null and b/_module/ncs/pe_crafting.ncs differ diff --git a/_module/ncs/pe_debug.ncs b/_module/ncs/pe_debug.ncs new file mode 100644 index 00000000..dd5e56f7 Binary files /dev/null and b/_module/ncs/pe_debug.ncs differ diff --git a/_module/ncs/pe_henchmen.ncs b/_module/ncs/pe_henchmen.ncs new file mode 100644 index 00000000..7a61d32b Binary files /dev/null and b/_module/ncs/pe_henchmen.ncs differ diff --git a/_module/ncs/ped_ck_air.ncs b/_module/ncs/ped_ck_air.ncs index 73a92294..783fdbeb 100644 Binary files a/_module/ncs/ped_ck_air.ncs and b/_module/ncs/ped_ck_air.ncs differ diff --git a/_module/ncs/ped_ck_earth.ncs b/_module/ncs/ped_ck_earth.ncs index 93a110fc..e6aa401d 100644 Binary files a/_module/ncs/ped_ck_earth.ncs and b/_module/ncs/ped_ck_earth.ncs differ diff --git a/_module/ncs/ped_ck_fire.ncs b/_module/ncs/ped_ck_fire.ncs index 3ffc3b64..e4c06451 100644 Binary files a/_module/ncs/ped_ck_fire.ncs and b/_module/ncs/ped_ck_fire.ncs differ diff --git a/_module/ncs/ped_ck_haven.ncs b/_module/ncs/ped_ck_haven.ncs index d8a01d03..cb8bad77 100644 Binary files a/_module/ncs/ped_ck_haven.ncs and b/_module/ncs/ped_ck_haven.ncs differ diff --git a/_module/ncs/ped_ck_water.ncs b/_module/ncs/ped_ck_water.ncs index cff6580f..648049e3 100644 Binary files a/_module/ncs/ped_ck_water.ncs and b/_module/ncs/ped_ck_water.ncs differ diff --git a/_module/ncs/pedestal_speak.ncs b/_module/ncs/pedestal_speak.ncs index b3b9499d..1285b23b 100644 Binary files a/_module/ncs/pedestal_speak.ncs and b/_module/ncs/pedestal_speak.ncs differ diff --git a/_module/ncs/percive_thief.ncs b/_module/ncs/percive_thief.ncs index c0afd988..894347fc 100644 Binary files a/_module/ncs/percive_thief.ncs and b/_module/ncs/percive_thief.ncs differ diff --git a/_module/ncs/perem_spawn.ncs b/_module/ncs/perem_spawn.ncs index 89805ffe..3b778b0f 100644 Binary files a/_module/ncs/perem_spawn.ncs and b/_module/ncs/perem_spawn.ncs differ diff --git a/_module/ncs/perem_start_talk.ncs b/_module/ncs/perem_start_talk.ncs index a8efa2f9..82557def 100644 Binary files a/_module/ncs/perem_start_talk.ncs and b/_module/ncs/perem_start_talk.ncs differ diff --git a/_module/ncs/perem_userd.ncs b/_module/ncs/perem_userd.ncs index 206d2ee4..10226e13 100644 Binary files a/_module/ncs/perem_userd.ncs and b/_module/ncs/perem_userd.ncs differ diff --git a/_module/ncs/pi_buffing.ncs b/_module/ncs/pi_buffing.ncs new file mode 100644 index 00000000..e654f655 Binary files /dev/null and b/_module/ncs/pi_buffing.ncs differ diff --git a/_module/ncs/pi_crafting.ncs b/_module/ncs/pi_crafting.ncs new file mode 100644 index 00000000..7ba191b5 Binary files /dev/null and b/_module/ncs/pi_crafting.ncs differ diff --git a/_module/ncs/pi_debug.ncs b/_module/ncs/pi_debug.ncs new file mode 100644 index 00000000..6230c0d2 Binary files /dev/null and b/_module/ncs/pi_debug.ncs differ diff --git a/_module/ncs/pi_henchmen.ncs b/_module/ncs/pi_henchmen.ncs new file mode 100644 index 00000000..f9c4726f Binary files /dev/null and b/_module/ncs/pi_henchmen.ncs differ diff --git a/_module/ncs/pillar_death1.ncs b/_module/ncs/pillar_death1.ncs index c1b3316c..cbc98ca7 100644 Binary files a/_module/ncs/pillar_death1.ncs and b/_module/ncs/pillar_death1.ncs differ diff --git a/_module/ncs/pipes_peace.ncs b/_module/ncs/pipes_peace.ncs index 443f8713..fcf0fac2 100644 Binary files a/_module/ncs/pipes_peace.ncs and b/_module/ncs/pipes_peace.ncs differ diff --git a/_module/ncs/player_list.ncs b/_module/ncs/player_list.ncs index 88ba42ff..29d470cd 100644 Binary files a/_module/ncs/player_list.ncs and b/_module/ncs/player_list.ncs differ diff --git a/_module/ncs/player_rest.ncs b/_module/ncs/player_rest.ncs index 4ee1b0ee..539b3ffd 100644 Binary files a/_module/ncs/player_rest.ncs and b/_module/ncs/player_rest.ncs differ diff --git a/_module/ncs/port_irda.ncs b/_module/ncs/port_irda.ncs index f43712be..6f552704 100644 Binary files a/_module/ncs/port_irda.ncs and b/_module/ncs/port_irda.ncs differ diff --git a/_module/ncs/port_nasgarth.ncs b/_module/ncs/port_nasgarth.ncs index ceaee26d..62eb0b1a 100644 Binary files a/_module/ncs/port_nasgarth.ncs and b/_module/ncs/port_nasgarth.ncs differ diff --git a/_module/ncs/port_rift.ncs b/_module/ncs/port_rift.ncs index bf48e627..4c5c2b6f 100644 Binary files a/_module/ncs/port_rift.ncs and b/_module/ncs/port_rift.ncs differ diff --git a/_module/ncs/prc_pwondeath.ncs b/_module/ncs/prc_pwondeath.ncs index 7bba7ee1..29c0e053 100644 Binary files a/_module/ncs/prc_pwondeath.ncs and b/_module/ncs/prc_pwondeath.ncs differ diff --git a/_module/ncs/prc_pwonspawn.ncs b/_module/ncs/prc_pwonspawn.ncs index b6f445a7..5dcb51e1 100644 Binary files a/_module/ncs/prc_pwonspawn.ncs and b/_module/ncs/prc_pwonspawn.ncs differ diff --git a/_module/ncs/prc_rest.ncs b/_module/ncs/prc_rest.ncs index 3d9a0ce5..cd706adf 100644 Binary files a/_module/ncs/prc_rest.ncs and b/_module/ncs/prc_rest.ncs differ diff --git a/_module/ncs/pro_dance.ncs b/_module/ncs/pro_dance.ncs index aac041a5..4f12c90e 100644 Binary files a/_module/ncs/pro_dance.ncs and b/_module/ncs/pro_dance.ncs differ diff --git a/_module/ncs/puke.ncs b/_module/ncs/puke.ncs index 98574d35..a60d3b8c 100644 Binary files a/_module/ncs/puke.ncs and b/_module/ncs/puke.ncs differ diff --git a/_module/ncs/pureclass_check.ncs b/_module/ncs/pureclass_check.ncs index 5313c9d1..d1fc53a6 100644 Binary files a/_module/ncs/pureclass_check.ncs and b/_module/ncs/pureclass_check.ncs differ diff --git a/_module/ncs/pw_sp_troll.ncs b/_module/ncs/pw_sp_troll.ncs index 4a61650e..7c4cd9ed 100644 Binary files a/_module/ncs/pw_sp_troll.ncs and b/_module/ncs/pw_sp_troll.ncs differ diff --git a/_module/ncs/pw_ude_troll.ncs b/_module/ncs/pw_ude_troll.ncs index 1f9aff65..a382410f 100644 Binary files a/_module/ncs/pw_ude_troll.ncs and b/_module/ncs/pw_ude_troll.ncs differ diff --git a/_module/ncs/queen_sit.ncs b/_module/ncs/queen_sit.ncs index c84a36c0..958577d3 100644 Binary files a/_module/ncs/queen_sit.ncs and b/_module/ncs/queen_sit.ncs differ diff --git a/_module/ncs/queenspeak.ncs b/_module/ncs/queenspeak.ncs index cf193c01..498e7d98 100644 Binary files a/_module/ncs/queenspeak.ncs and b/_module/ncs/queenspeak.ncs differ diff --git a/_module/ncs/random_drop.ncs b/_module/ncs/random_drop.ncs index 29e19baa..876d71ce 100644 Binary files a/_module/ncs/random_drop.ncs and b/_module/ncs/random_drop.ncs differ diff --git a/_module/ncs/random_drop2.ncs b/_module/ncs/random_drop2.ncs index b0e9db7e..5e974455 100644 Binary files a/_module/ncs/random_drop2.ncs and b/_module/ncs/random_drop2.ncs differ diff --git a/_module/ncs/random_drop_yy.ncs b/_module/ncs/random_drop_yy.ncs index 5701e15c..189cd1b3 100644 Binary files a/_module/ncs/random_drop_yy.ncs and b/_module/ncs/random_drop_yy.ncs differ diff --git a/_module/ncs/range_att_spawn.ncs b/_module/ncs/range_att_spawn.ncs index 74623d00..3985e3fb 100644 Binary files a/_module/ncs/range_att_spawn.ncs and b/_module/ncs/range_att_spawn.ncs differ diff --git a/_module/ncs/re_com_userdef.ncs b/_module/ncs/re_com_userdef.ncs index 34d85eaa..0390a846 100644 Binary files a/_module/ncs/re_com_userdef.ncs and b/_module/ncs/re_com_userdef.ncs differ diff --git a/_module/ncs/re_common_blkd.ncs b/_module/ncs/re_common_blkd.ncs index 54f253a3..b5d92db7 100644 Binary files a/_module/ncs/re_common_blkd.ncs and b/_module/ncs/re_common_blkd.ncs differ diff --git a/_module/ncs/re_common_spawn.ncs b/_module/ncs/re_common_spawn.ncs index 26e451a4..8ce39697 100644 Binary files a/_module/ncs/re_common_spawn.ncs and b/_module/ncs/re_common_spawn.ncs differ diff --git a/_module/ncs/re_comspawn_sitb.ncs b/_module/ncs/re_comspawn_sitb.ncs index 2764d21b..bd5ec8c3 100644 Binary files a/_module/ncs/re_comspawn_sitb.ncs and b/_module/ncs/re_comspawn_sitb.ncs differ diff --git a/_module/ncs/re_comspawn_sitc.ncs b/_module/ncs/re_comspawn_sitc.ncs index 8ad85875..cc8b227e 100644 Binary files a/_module/ncs/re_comspawn_sitc.ncs and b/_module/ncs/re_comspawn_sitc.ncs differ diff --git a/_module/ncs/re_comspawn_sits.ncs b/_module/ncs/re_comspawn_sits.ncs index db731808..02d75d67 100644 Binary files a/_module/ncs/re_comspawn_sits.ncs and b/_module/ncs/re_comspawn_sits.ncs differ diff --git a/_module/ncs/re_comspawn_wayp.ncs b/_module/ncs/re_comspawn_wayp.ncs index fc40755d..803d6373 100644 Binary files a/_module/ncs/re_comspawn_wayp.ncs and b/_module/ncs/re_comspawn_wayp.ncs differ diff --git a/_module/ncs/re_comspawnfarm1.ncs b/_module/ncs/re_comspawnfarm1.ncs index 2e2934dd..27e2050a 100644 Binary files a/_module/ncs/re_comspawnfarm1.ncs and b/_module/ncs/re_comspawnfarm1.ncs differ diff --git a/_module/ncs/re_comspawnfarm2.ncs b/_module/ncs/re_comspawnfarm2.ncs index e989b81e..6957a20e 100644 Binary files a/_module/ncs/re_comspawnfarm2.ncs and b/_module/ncs/re_comspawnfarm2.ncs differ diff --git a/_module/ncs/re_comspawnfarm3.ncs b/_module/ncs/re_comspawnfarm3.ncs index 1f0ae7a5..d7641f5b 100644 Binary files a/_module/ncs/re_comspawnfarm3.ncs and b/_module/ncs/re_comspawnfarm3.ncs differ diff --git a/_module/ncs/re_comspawnfarm4.ncs b/_module/ncs/re_comspawnfarm4.ncs index 84d5fba5..d5464ab5 100644 Binary files a/_module/ncs/re_comspawnfarm4.ncs and b/_module/ncs/re_comspawnfarm4.ncs differ diff --git a/_module/ncs/re_comspawnfarm5.ncs b/_module/ncs/re_comspawnfarm5.ncs index 0dc5cf79..32b4a971 100644 Binary files a/_module/ncs/re_comspawnfarm5.ncs and b/_module/ncs/re_comspawnfarm5.ncs differ diff --git a/_module/ncs/re_rndwayp_go.ncs b/_module/ncs/re_rndwayp_go.ncs index 782997bd..3f28d814 100644 Binary files a/_module/ncs/re_rndwayp_go.ncs and b/_module/ncs/re_rndwayp_go.ncs differ diff --git a/_module/ncs/ref_int1.ncs b/_module/ncs/ref_int1.ncs index 865ea5e7..6bbde1be 100644 Binary files a/_module/ncs/ref_int1.ncs and b/_module/ncs/ref_int1.ncs differ diff --git a/_module/ncs/ref_int3.ncs b/_module/ncs/ref_int3.ncs index f3a16cee..1a9869b9 100644 Binary files a/_module/ncs/ref_int3.ncs and b/_module/ncs/ref_int3.ncs differ diff --git a/_module/ncs/removedragon.ncs b/_module/ncs/removedragon.ncs index d4a5b6d2..e44c05a5 100644 Binary files a/_module/ncs/removedragon.ncs and b/_module/ncs/removedragon.ncs differ diff --git a/_module/ncs/rh_ambush.ncs b/_module/ncs/rh_ambush.ncs index 37dfe6f4..2cf242d7 100644 Binary files a/_module/ncs/rh_ambush.ncs and b/_module/ncs/rh_ambush.ncs differ diff --git a/_module/ncs/rh_bladetrap.ncs b/_module/ncs/rh_bladetrap.ncs index b72d74b5..7b88a71c 100644 Binary files a/_module/ncs/rh_bladetrap.ncs and b/_module/ncs/rh_bladetrap.ncs differ diff --git a/_module/ncs/rh_golem_attack.ncs b/_module/ncs/rh_golem_attack.ncs index 89ca3bb1..9d476267 100644 Binary files a/_module/ncs/rh_golem_attack.ncs and b/_module/ncs/rh_golem_attack.ncs differ diff --git a/_module/ncs/rh_mirror_attack.ncs b/_module/ncs/rh_mirror_attack.ncs index c3543813..eff4ff87 100644 Binary files a/_module/ncs/rh_mirror_attack.ncs and b/_module/ncs/rh_mirror_attack.ncs differ diff --git a/_module/ncs/rh_musicstop.ncs b/_module/ncs/rh_musicstop.ncs index cea3a987..22504506 100644 Binary files a/_module/ncs/rh_musicstop.ncs and b/_module/ncs/rh_musicstop.ncs differ diff --git a/_module/ncs/rhgolem_buff.ncs b/_module/ncs/rhgolem_buff.ncs index 8ce669f7..8453464b 100644 Binary files a/_module/ncs/rhgolem_buff.ncs and b/_module/ncs/rhgolem_buff.ncs differ diff --git a/_module/ncs/rhgolem_charm.ncs b/_module/ncs/rhgolem_charm.ncs index 0dd04c4e..c7dedbf2 100644 Binary files a/_module/ncs/rhgolem_charm.ncs and b/_module/ncs/rhgolem_charm.ncs differ diff --git a/_module/ncs/rhgolem_threaten.ncs b/_module/ncs/rhgolem_threaten.ncs index 35896be3..0450bc44 100644 Binary files a/_module/ncs/rhgolem_threaten.ncs and b/_module/ncs/rhgolem_threaten.ncs differ diff --git a/_module/ncs/rift_e_enter.ncs b/_module/ncs/rift_e_enter.ncs index efe62ddc..d4d33e57 100644 Binary files a/_module/ncs/rift_e_enter.ncs and b/_module/ncs/rift_e_enter.ncs differ diff --git a/_module/ncs/rift_e_exit.ncs b/_module/ncs/rift_e_exit.ncs index e6aa031b..ee9efc51 100644 Binary files a/_module/ncs/rift_e_exit.ncs and b/_module/ncs/rift_e_exit.ncs differ diff --git a/_module/ncs/rift_enter.ncs b/_module/ncs/rift_enter.ncs index 7ce5bb3f..ea3d2175 100644 Binary files a/_module/ncs/rift_enter.ncs and b/_module/ncs/rift_enter.ncs differ diff --git a/_module/ncs/rift_exit.ncs b/_module/ncs/rift_exit.ncs index 53fdc2f1..b1095eef 100644 Binary files a/_module/ncs/rift_exit.ncs and b/_module/ncs/rift_exit.ncs differ diff --git a/_module/ncs/rift_g_enter.ncs b/_module/ncs/rift_g_enter.ncs index 26771caa..6defd66b 100644 Binary files a/_module/ncs/rift_g_enter.ncs and b/_module/ncs/rift_g_enter.ncs differ diff --git a/_module/ncs/rift_g_exit.ncs b/_module/ncs/rift_g_exit.ncs index e4a95faf..9dc0d895 100644 Binary files a/_module/ncs/rift_g_exit.ncs and b/_module/ncs/rift_g_exit.ncs differ diff --git a/_module/ncs/rift_n_enter.ncs b/_module/ncs/rift_n_enter.ncs index c1f5f6ee..fac0c181 100644 Binary files a/_module/ncs/rift_n_enter.ncs and b/_module/ncs/rift_n_enter.ncs differ diff --git a/_module/ncs/rift_n_exit.ncs b/_module/ncs/rift_n_exit.ncs index 82251555..e10aa729 100644 Binary files a/_module/ncs/rift_n_exit.ncs and b/_module/ncs/rift_n_exit.ncs differ diff --git a/_module/ncs/rift_port_in.ncs b/_module/ncs/rift_port_in.ncs index f5afd02b..f09cd02e 100644 Binary files a/_module/ncs/rift_port_in.ncs and b/_module/ncs/rift_port_in.ncs differ diff --git a/_module/ncs/riftchest_abyss.ncs b/_module/ncs/riftchest_abyss.ncs index 4e8ba464..ce526ff8 100644 Binary files a/_module/ncs/riftchest_abyss.ncs and b/_module/ncs/riftchest_abyss.ncs differ diff --git a/_module/ncs/riftchest_archon.ncs b/_module/ncs/riftchest_archon.ncs index 25ec7076..2d70c30e 100644 Binary files a/_module/ncs/riftchest_archon.ncs and b/_module/ncs/riftchest_archon.ncs differ diff --git a/_module/ncs/riftchest_celest.ncs b/_module/ncs/riftchest_celest.ncs index 24748c0e..4d044c20 100644 Binary files a/_module/ncs/riftchest_celest.ncs and b/_module/ncs/riftchest_celest.ncs differ diff --git a/_module/ncs/rogue_bard_ck.ncs b/_module/ncs/rogue_bard_ck.ncs index 818114eb..691ada92 100644 Binary files a/_module/ncs/rogue_bard_ck.ncs and b/_module/ncs/rogue_bard_ck.ncs differ diff --git a/_module/ncs/rogue_sign1.ncs b/_module/ncs/rogue_sign1.ncs index 3313fb89..61f483cb 100644 Binary files a/_module/ncs/rogue_sign1.ncs and b/_module/ncs/rogue_sign1.ncs differ diff --git a/_module/ncs/rogue_sign2.ncs b/_module/ncs/rogue_sign2.ncs index bad86c0c..eb7789a2 100644 Binary files a/_module/ncs/rogue_sign2.ncs and b/_module/ncs/rogue_sign2.ncs differ diff --git a/_module/ncs/royalsealchk.ncs b/_module/ncs/royalsealchk.ncs index f0628b34..8d8c6e96 100644 Binary files a/_module/ncs/royalsealchk.ncs and b/_module/ncs/royalsealchk.ncs differ diff --git a/_module/ncs/royalsealtake.ncs b/_module/ncs/royalsealtake.ncs index c5d4453f..c6c04bd9 100644 Binary files a/_module/ncs/royalsealtake.ncs and b/_module/ncs/royalsealtake.ncs differ diff --git a/_module/ncs/sarum_ck2.ncs b/_module/ncs/sarum_ck2.ncs index 1059340a..3333d378 100644 Binary files a/_module/ncs/sarum_ck2.ncs and b/_module/ncs/sarum_ck2.ncs differ diff --git a/_module/ncs/sarum_guard_gv1.ncs b/_module/ncs/sarum_guard_gv1.ncs index b28d1e7f..d7cc1d1a 100644 Binary files a/_module/ncs/sarum_guard_gv1.ncs and b/_module/ncs/sarum_guard_gv1.ncs differ diff --git a/_module/ncs/sarumguard_give.ncs b/_module/ncs/sarumguard_give.ncs index cd25f806..cd5101c5 100644 Binary files a/_module/ncs/sarumguard_give.ncs and b/_module/ncs/sarumguard_give.ncs differ diff --git a/_module/ncs/sarumguardckvar2.ncs b/_module/ncs/sarumguardckvar2.ncs index c4d8116b..d4c9e3b2 100644 Binary files a/_module/ncs/sarumguardckvar2.ncs and b/_module/ncs/sarumguardckvar2.ncs differ diff --git a/_module/ncs/sc_ra_rnd_dialog.ncs b/_module/ncs/sc_ra_rnd_dialog.ncs index 898eb9d0..d32f1f8e 100644 Binary files a/_module/ncs/sc_ra_rnd_dialog.ncs and b/_module/ncs/sc_ra_rnd_dialog.ncs differ diff --git a/_module/ncs/sc_ra_rnd_rumors.ncs b/_module/ncs/sc_ra_rnd_rumors.ncs index 6d8ca412..4366a139 100644 Binary files a/_module/ncs/sc_ra_rnd_rumors.ncs and b/_module/ncs/sc_ra_rnd_rumors.ncs differ diff --git a/_module/ncs/scalesize.ncs b/_module/ncs/scalesize.ncs index 50000feb..170ebe0f 100644 Binary files a/_module/ncs/scalesize.ncs and b/_module/ncs/scalesize.ncs differ diff --git a/_module/ncs/sec_dr_abandpass.ncs b/_module/ncs/sec_dr_abandpass.ncs index 13fe6c5e..b328039d 100644 Binary files a/_module/ncs/sec_dr_abandpass.ncs and b/_module/ncs/sec_dr_abandpass.ncs differ diff --git a/_module/ncs/sec_dr_hauntcave.ncs b/_module/ncs/sec_dr_hauntcave.ncs index 02e6ebfc..2460ae79 100644 Binary files a/_module/ncs/sec_dr_hauntcave.ncs and b/_module/ncs/sec_dr_hauntcave.ncs differ diff --git a/_module/ncs/sei_pickup.ncs b/_module/ncs/sei_pickup.ncs index 6532bbf2..7246e273 100644 Binary files a/_module/ncs/sei_pickup.ncs and b/_module/ncs/sei_pickup.ncs differ diff --git a/_module/ncs/sei_sit.ncs b/_module/ncs/sei_sit.ncs index de30a5f7..794e5f14 100644 Binary files a/_module/ncs/sei_sit.ncs and b/_module/ncs/sei_sit.ncs differ diff --git a/_module/ncs/sei_ta_isoccu.ncs b/_module/ncs/sei_ta_isoccu.ncs index f2e20626..d6a72af3 100644 Binary files a/_module/ncs/sei_ta_isoccu.ncs and b/_module/ncs/sei_ta_isoccu.ncs differ diff --git a/_module/ncs/set_ang_dragon_1.ncs b/_module/ncs/set_ang_dragon_1.ncs index 66486e62..45cf2b81 100644 Binary files a/_module/ncs/set_ang_dragon_1.ncs and b/_module/ncs/set_ang_dragon_1.ncs differ diff --git a/_module/ncs/set_ang_dragon_2.ncs b/_module/ncs/set_ang_dragon_2.ncs index 92a6092f..f8474734 100644 Binary files a/_module/ncs/set_ang_dragon_2.ncs and b/_module/ncs/set_ang_dragon_2.ncs differ diff --git a/_module/ncs/set_dryad_1.ncs b/_module/ncs/set_dryad_1.ncs index 8e680028..f29bd8bb 100644 Binary files a/_module/ncs/set_dryad_1.ncs and b/_module/ncs/set_dryad_1.ncs differ diff --git a/_module/ncs/set_ygdragon_1.ncs b/_module/ncs/set_ygdragon_1.ncs index cd4de48c..d0ce9ecc 100644 Binary files a/_module/ncs/set_ygdragon_1.ncs and b/_module/ncs/set_ygdragon_1.ncs differ diff --git a/_module/ncs/shame_ck.ncs b/_module/ncs/shame_ck.ncs index 79daa2d9..547965ab 100644 Binary files a/_module/ncs/shame_ck.ncs and b/_module/ncs/shame_ck.ncs differ diff --git a/_module/ncs/shaolin_done.ncs b/_module/ncs/shaolin_done.ncs index 4f7bdcce..bb1a0556 100644 Binary files a/_module/ncs/shaolin_done.ncs and b/_module/ncs/shaolin_done.ncs differ diff --git a/_module/ncs/shaolin_setvar.ncs b/_module/ncs/shaolin_setvar.ncs index 88622ab9..aecdd00a 100644 Binary files a/_module/ncs/shaolin_setvar.ncs and b/_module/ncs/shaolin_setvar.ncs differ diff --git a/_module/ncs/shaolin_spoken2.ncs b/_module/ncs/shaolin_spoken2.ncs index a0d2d0c4..d4bbb29c 100644 Binary files a/_module/ncs/shaolin_spoken2.ncs and b/_module/ncs/shaolin_spoken2.ncs differ diff --git a/_module/ncs/sitchair.ncs b/_module/ncs/sitchair.ncs index 3c973a66..719622f1 100644 Binary files a/_module/ncs/sitchair.ncs and b/_module/ncs/sitchair.ncs differ diff --git a/_module/ncs/sitchairbreak.ncs b/_module/ncs/sitchairbreak.ncs index e41cf291..c2daf2e1 100644 Binary files a/_module/ncs/sitchairbreak.ncs and b/_module/ncs/sitchairbreak.ncs differ diff --git a/_module/ncs/sitchairfix.ncs b/_module/ncs/sitchairfix.ncs index bce94f6f..e85cbb58 100644 Binary files a/_module/ncs/sitchairfix.ncs and b/_module/ncs/sitchairfix.ncs differ diff --git a/_module/ncs/sitinchair.ncs b/_module/ncs/sitinchair.ncs index 607923d2..22ed7777 100644 Binary files a/_module/ncs/sitinchair.ncs and b/_module/ncs/sitinchair.ncs differ diff --git a/_module/ncs/sitspeak.ncs b/_module/ncs/sitspeak.ncs index 07a0ace0..a7d3af26 100644 Binary files a/_module/ncs/sitspeak.ncs and b/_module/ncs/sitspeak.ncs differ diff --git a/_module/ncs/sitwoodbench.ncs b/_module/ncs/sitwoodbench.ncs index 2248b7e4..06f8a8d2 100644 Binary files a/_module/ncs/sitwoodbench.ncs and b/_module/ncs/sitwoodbench.ncs differ diff --git a/_module/ncs/skull_blue_score.ncs b/_module/ncs/skull_blue_score.ncs index 9c09ff0e..5d67a86f 100644 Binary files a/_module/ncs/skull_blue_score.ncs and b/_module/ncs/skull_blue_score.ncs differ diff --git a/_module/ncs/skull_final.ncs b/_module/ncs/skull_final.ncs index af614c7f..29f3cf04 100644 Binary files a/_module/ncs/skull_final.ncs and b/_module/ncs/skull_final.ncs differ diff --git a/_module/ncs/skull_red_score.ncs b/_module/ncs/skull_red_score.ncs index fb9fb6bd..1b2a4f77 100644 Binary files a/_module/ncs/skull_red_score.ncs and b/_module/ncs/skull_red_score.ncs differ diff --git a/_module/ncs/skull_reset_scor.ncs b/_module/ncs/skull_reset_scor.ncs index d3ac294e..1d6d5336 100644 Binary files a/_module/ncs/skull_reset_scor.ncs and b/_module/ncs/skull_reset_scor.ncs differ diff --git a/_module/ncs/skullball_strip.ncs b/_module/ncs/skullball_strip.ncs index 1f17b14e..cec1ad8e 100644 Binary files a/_module/ncs/skullball_strip.ncs and b/_module/ncs/skullball_strip.ncs differ diff --git a/_module/ncs/sot.ncs b/_module/ncs/sot.ncs index 5b7fbd83..5fa781f0 100644 Binary files a/_module/ncs/sot.ncs and b/_module/ncs/sot.ncs differ diff --git a/_module/ncs/sot_back_beacon.ncs b/_module/ncs/sot_back_beacon.ncs index c2c5516a..c60fef86 100644 Binary files a/_module/ncs/sot_back_beacon.ncs and b/_module/ncs/sot_back_beacon.ncs differ diff --git a/_module/ncs/sot_beacon.ncs b/_module/ncs/sot_beacon.ncs index 08aac958..70ccde11 100644 Binary files a/_module/ncs/sot_beacon.ncs and b/_module/ncs/sot_beacon.ncs differ diff --git a/_module/ncs/sot_home.ncs b/_module/ncs/sot_home.ncs index eb630ed0..ad6633ae 100644 Binary files a/_module/ncs/sot_home.ncs and b/_module/ncs/sot_home.ncs differ diff --git a/_module/ncs/sot_last_use.ncs b/_module/ncs/sot_last_use.ncs index 4fd1d469..150d3738 100644 Binary files a/_module/ncs/sot_last_use.ncs and b/_module/ncs/sot_last_use.ncs differ diff --git a/_module/ncs/spade.ncs b/_module/ncs/spade.ncs index 6b17f07d..b45207fa 100644 Binary files a/_module/ncs/spade.ncs and b/_module/ncs/spade.ncs differ diff --git a/_module/ncs/spawn_corpse_dcy.ncs b/_module/ncs/spawn_corpse_dcy.ncs index a88991b2..a837e50e 100644 Binary files a/_module/ncs/spawn_corpse_dcy.ncs and b/_module/ncs/spawn_corpse_dcy.ncs differ diff --git a/_module/ncs/spawn_corpse_dth.ncs b/_module/ncs/spawn_corpse_dth.ncs index 06e4cd76..b0153208 100644 Binary files a/_module/ncs/spawn_corpse_dth.ncs and b/_module/ncs/spawn_corpse_dth.ncs differ diff --git a/_module/ncs/spawn_dist_corps.ncs b/_module/ncs/spawn_dist_corps.ncs index 3d966736..d3a011e9 100644 Binary files a/_module/ncs/spawn_dist_corps.ncs and b/_module/ncs/spawn_dist_corps.ncs differ diff --git a/_module/ncs/spawn_oncloscrp.ncs b/_module/ncs/spawn_oncloscrp.ncs index 7b33f7de..69a2f74c 100644 Binary files a/_module/ncs/spawn_oncloscrp.ncs and b/_module/ncs/spawn_oncloscrp.ncs differ diff --git a/_module/ncs/spawn_orig_hb.ncs b/_module/ncs/spawn_orig_hb.ncs index 1ec60f87..58c1b943 100644 Binary files a/_module/ncs/spawn_orig_hb.ncs and b/_module/ncs/spawn_orig_hb.ncs differ diff --git a/_module/ncs/spawn_pseudohb.ncs b/_module/ncs/spawn_pseudohb.ncs index bedb4fbf..e15db41e 100644 Binary files a/_module/ncs/spawn_pseudohb.ncs and b/_module/ncs/spawn_pseudohb.ncs differ diff --git a/_module/ncs/spawn_sample_hb.ncs b/_module/ncs/spawn_sample_hb.ncs index 1ec60f87..58c1b943 100644 Binary files a/_module/ncs/spawn_sample_hb.ncs and b/_module/ncs/spawn_sample_hb.ncs differ diff --git a/_module/ncs/spawn_sc_cmptrig.ncs b/_module/ncs/spawn_sc_cmptrig.ncs index c86acfba..3708de8f 100644 Binary files a/_module/ncs/spawn_sc_cmptrig.ncs and b/_module/ncs/spawn_sc_cmptrig.ncs differ diff --git a/_module/ncs/spawn_sc_deactiv.ncs b/_module/ncs/spawn_sc_deactiv.ncs index bc617f8b..8027e576 100644 Binary files a/_module/ncs/spawn_sc_deactiv.ncs and b/_module/ncs/spawn_sc_deactiv.ncs differ diff --git a/_module/ncs/spawn_sc_death.ncs b/_module/ncs/spawn_sc_death.ncs index e7977121..a39358eb 100644 Binary files a/_module/ncs/spawn_sc_death.ncs and b/_module/ncs/spawn_sc_death.ncs differ diff --git a/_module/ncs/spawn_sc_hbeat.ncs b/_module/ncs/spawn_sc_hbeat.ncs index e1391375..1750172b 100644 Binary files a/_module/ncs/spawn_sc_hbeat.ncs and b/_module/ncs/spawn_sc_hbeat.ncs differ diff --git a/_module/ncs/spawn_sc_patrol.ncs b/_module/ncs/spawn_sc_patrol.ncs index 28490c7f..d4220413 100644 Binary files a/_module/ncs/spawn_sc_patrol.ncs and b/_module/ncs/spawn_sc_patrol.ncs differ diff --git a/_module/ncs/spawn_sc_spawn.ncs b/_module/ncs/spawn_sc_spawn.ncs index 21ed547b..547da034 100644 Binary files a/_module/ncs/spawn_sc_spawn.ncs and b/_module/ncs/spawn_sc_spawn.ncs differ diff --git a/_module/ncs/spawn_smpl_onen2.ncs b/_module/ncs/spawn_smpl_onen2.ncs index a8d66ccf..49087daa 100644 Binary files a/_module/ncs/spawn_smpl_onen2.ncs and b/_module/ncs/spawn_smpl_onen2.ncs differ diff --git a/_module/ncs/spawn_smpl_onent.ncs b/_module/ncs/spawn_smpl_onent.ncs index 2bafc7b1..7a3ec9bf 100644 Binary files a/_module/ncs/spawn_smpl_onent.ncs and b/_module/ncs/spawn_smpl_onent.ncs differ diff --git a/_module/ncs/spawn_smpl_onext.ncs b/_module/ncs/spawn_smpl_onext.ncs index 97ea11d0..c90b83d4 100644 Binary files a/_module/ncs/spawn_smpl_onext.ncs and b/_module/ncs/spawn_smpl_onext.ncs differ diff --git a/_module/ncs/spawn_statue.ncs b/_module/ncs/spawn_statue.ncs index eda5c76e..4b17a431 100644 Binary files a/_module/ncs/spawn_statue.ncs and b/_module/ncs/spawn_statue.ncs differ diff --git a/_module/ncs/spawn_statue2.ncs b/_module/ncs/spawn_statue2.ncs index a1d54e95..7676a6fe 100644 Binary files a/_module/ncs/spawn_statue2.ncs and b/_module/ncs/spawn_statue2.ncs differ diff --git a/_module/ncs/spawn_statue3.ncs b/_module/ncs/spawn_statue3.ncs index 66f1ca2c..b1e55e41 100644 Binary files a/_module/ncs/spawn_statue3.ncs and b/_module/ncs/spawn_statue3.ncs differ diff --git a/_module/ncs/spawn_valar.ncs b/_module/ncs/spawn_valar.ncs index edd4bfaa..ff2e2540 100644 Binary files a/_module/ncs/spawn_valar.ncs and b/_module/ncs/spawn_valar.ncs differ diff --git a/_module/ncs/spawnb_cc_activ.ncs b/_module/ncs/spawnb_cc_activ.ncs index de4d1662..0cc56a58 100644 Binary files a/_module/ncs/spawnb_cc_activ.ncs and b/_module/ncs/spawnb_cc_activ.ncs differ diff --git a/_module/ncs/spawnb_cc_dactiv.ncs b/_module/ncs/spawnb_cc_dactiv.ncs index 77e80d77..fc5e113b 100644 Binary files a/_module/ncs/spawnb_cc_dactiv.ncs and b/_module/ncs/spawnb_cc_dactiv.ncs differ diff --git a/_module/ncs/spawnb_cc_dump.ncs b/_module/ncs/spawnb_cc_dump.ncs index 361de373..f7543f71 100644 Binary files a/_module/ncs/spawnb_cc_dump.ncs and b/_module/ncs/spawnb_cc_dump.ncs differ diff --git a/_module/ncs/spawnb_cc_nodump.ncs b/_module/ncs/spawnb_cc_nodump.ncs index 58bdbf95..5b8f17e9 100644 Binary files a/_module/ncs/spawnb_cc_nodump.ncs and b/_module/ncs/spawnb_cc_nodump.ncs differ diff --git a/_module/ncs/spawnb_cc_notrck.ncs b/_module/ncs/spawnb_cc_notrck.ncs index d044ddef..58222a61 100644 Binary files a/_module/ncs/spawnb_cc_notrck.ncs and b/_module/ncs/spawnb_cc_notrck.ncs differ diff --git a/_module/ncs/spawnb_cc_nsclog.ncs b/_module/ncs/spawnb_cc_nsclog.ncs index 1da7b912..6fccacb7 100644 Binary files a/_module/ncs/spawnb_cc_nsclog.ncs and b/_module/ncs/spawnb_cc_nsclog.ncs differ diff --git a/_module/ncs/spawnb_cc_nsdlog.ncs b/_module/ncs/spawnb_cc_nsdlog.ncs index f5d87036..f7c0ecb0 100644 Binary files a/_module/ncs/spawnb_cc_nsdlog.ncs and b/_module/ncs/spawnb_cc_nsdlog.ncs differ diff --git a/_module/ncs/spawnb_cc_sclog.ncs b/_module/ncs/spawnb_cc_sclog.ncs index 08533e59..a54d13a7 100644 Binary files a/_module/ncs/spawnb_cc_sclog.ncs and b/_module/ncs/spawnb_cc_sclog.ncs differ diff --git a/_module/ncs/spawnb_cc_sdlog.ncs b/_module/ncs/spawnb_cc_sdlog.ncs index 718c9dca..3ee926a0 100644 Binary files a/_module/ncs/spawnb_cc_sdlog.ncs and b/_module/ncs/spawnb_cc_sdlog.ncs differ diff --git a/_module/ncs/spawnb_cc_trck.ncs b/_module/ncs/spawnb_cc_trck.ncs index b616030c..83c5cad5 100644 Binary files a/_module/ncs/spawnb_cc_trck.ncs and b/_module/ncs/spawnb_cc_trck.ncs differ diff --git a/_module/ncs/spawnb_sample_ai.ncs b/_module/ncs/spawnb_sample_ai.ncs index 9823929e..38bf1074 100644 Binary files a/_module/ncs/spawnb_sample_ai.ncs and b/_module/ncs/spawnb_sample_ai.ncs differ diff --git a/_module/ncs/spawnb_sc_activ.ncs b/_module/ncs/spawnb_sc_activ.ncs index d932f403..830f5826 100644 Binary files a/_module/ncs/spawnb_sc_activ.ncs and b/_module/ncs/spawnb_sc_activ.ncs differ diff --git a/_module/ncs/spawnb_sc_dactiv.ncs b/_module/ncs/spawnb_sc_dactiv.ncs index 9859011f..32866346 100644 Binary files a/_module/ncs/spawnb_sc_dactiv.ncs and b/_module/ncs/spawnb_sc_dactiv.ncs differ diff --git a/_module/ncs/spawnb_sc_dump.ncs b/_module/ncs/spawnb_sc_dump.ncs index 79833875..070b4bd0 100644 Binary files a/_module/ncs/spawnb_sc_dump.ncs and b/_module/ncs/spawnb_sc_dump.ncs differ diff --git a/_module/ncs/spawnb_sc_nodump.ncs b/_module/ncs/spawnb_sc_nodump.ncs index 668b2fe6..69ee30d2 100644 Binary files a/_module/ncs/spawnb_sc_nodump.ncs and b/_module/ncs/spawnb_sc_nodump.ncs differ diff --git a/_module/ncs/spawnb_sc_notrck.ncs b/_module/ncs/spawnb_sc_notrck.ncs index 417f948f..13dd03f4 100644 Binary files a/_module/ncs/spawnb_sc_notrck.ncs and b/_module/ncs/spawnb_sc_notrck.ncs differ diff --git a/_module/ncs/spawnb_sc_nsdlog.ncs b/_module/ncs/spawnb_sc_nsdlog.ncs index fdf16348..e6cd4a47 100644 Binary files a/_module/ncs/spawnb_sc_nsdlog.ncs and b/_module/ncs/spawnb_sc_nsdlog.ncs differ diff --git a/_module/ncs/spawnb_sc_sclog.ncs b/_module/ncs/spawnb_sc_sclog.ncs index db1b06a3..7ccdc174 100644 Binary files a/_module/ncs/spawnb_sc_sclog.ncs and b/_module/ncs/spawnb_sc_sclog.ncs differ diff --git a/_module/ncs/spawnb_sc_sdlog.ncs b/_module/ncs/spawnb_sc_sdlog.ncs index dcb9323c..3c372438 100644 Binary files a/_module/ncs/spawnb_sc_sdlog.ncs and b/_module/ncs/spawnb_sc_sdlog.ncs differ diff --git a/_module/ncs/spawnb_sc_snclog.ncs b/_module/ncs/spawnb_sc_snclog.ncs index f53ea125..68c0e660 100644 Binary files a/_module/ncs/spawnb_sc_snclog.ncs and b/_module/ncs/spawnb_sc_snclog.ncs differ diff --git a/_module/ncs/spawnb_sc_trck.ncs b/_module/ncs/spawnb_sc_trck.ncs index ba787d5b..36b0ece1 100644 Binary files a/_module/ncs/spawnb_sc_trck.ncs and b/_module/ncs/spawnb_sc_trck.ncs differ diff --git a/_module/ncs/spawnbanner.ncs b/_module/ncs/spawnbanner.ncs index c3182260..da7b1d60 100644 Binary files a/_module/ncs/spawnbanner.ncs and b/_module/ncs/spawnbanner.ncs differ diff --git a/_module/ncs/spawndaemon.ncs b/_module/ncs/spawndaemon.ncs index 894d5159..a5bbf567 100644 Binary files a/_module/ncs/spawndaemon.ncs and b/_module/ncs/spawndaemon.ncs differ diff --git a/_module/ncs/speak_sit_spawn.ncs b/_module/ncs/speak_sit_spawn.ncs index fc2233e9..7bf1a084 100644 Binary files a/_module/ncs/speak_sit_spawn.ncs and b/_module/ncs/speak_sit_spawn.ncs differ diff --git a/_module/ncs/start_convo1.ncs b/_module/ncs/start_convo1.ncs index db5722a1..6180fc00 100644 Binary files a/_module/ncs/start_convo1.ncs and b/_module/ncs/start_convo1.ncs differ diff --git a/_module/ncs/stealth_cloak.ncs b/_module/ncs/stealth_cloak.ncs index ad5d4971..e9d8acbe 100644 Binary files a/_module/ncs/stealth_cloak.ncs and b/_module/ncs/stealth_cloak.ncs differ diff --git a/_module/ncs/stone_sphinx.ncs b/_module/ncs/stone_sphinx.ncs index 5a38976f..367409c6 100644 Binary files a/_module/ncs/stone_sphinx.ncs and b/_module/ncs/stone_sphinx.ncs differ diff --git a/_module/ncs/svirf_ck_race.ncs b/_module/ncs/svirf_ck_race.ncs index 900b764c..4a584dbf 100644 Binary files a/_module/ncs/svirf_ck_race.ncs and b/_module/ncs/svirf_ck_race.ncs differ diff --git a/_module/ncs/svirf_common_ck.ncs b/_module/ncs/svirf_common_ck.ncs index ca1d7401..99c080f6 100644 Binary files a/_module/ncs/svirf_common_ck.ncs and b/_module/ncs/svirf_common_ck.ncs differ diff --git a/_module/ncs/svirf_hostile_ck.ncs b/_module/ncs/svirf_hostile_ck.ncs index 09a42ce8..9e8c6e5c 100644 Binary files a/_module/ncs/svirf_hostile_ck.ncs and b/_module/ncs/svirf_hostile_ck.ncs differ diff --git a/_module/ncs/svirf_unlock.ncs b/_module/ncs/svirf_unlock.ncs index fdbd642c..31680757 100644 Binary files a/_module/ncs/svirf_unlock.ncs and b/_module/ncs/svirf_unlock.ncs differ diff --git a/_module/ncs/svirfbouncer_gv1.ncs b/_module/ncs/svirfbouncer_gv1.ncs index db80f2d1..a1cc569c 100644 Binary files a/_module/ncs/svirfbouncer_gv1.ncs and b/_module/ncs/svirfbouncer_gv1.ncs differ diff --git a/_module/ncs/svirfbouncer_gv2.ncs b/_module/ncs/svirfbouncer_gv2.ncs index 7af92f23..3cb033ca 100644 Binary files a/_module/ncs/svirfbouncer_gv2.ncs and b/_module/ncs/svirfbouncer_gv2.ncs differ diff --git a/_module/ncs/svirfbryan_gv1.ncs b/_module/ncs/svirfbryan_gv1.ncs index 8e6df842..78879720 100644 Binary files a/_module/ncs/svirfbryan_gv1.ncs and b/_module/ncs/svirfbryan_gv1.ncs differ diff --git a/_module/ncs/take_dryad_boots.ncs b/_module/ncs/take_dryad_boots.ncs index 0250d36f..15f572d4 100644 Binary files a/_module/ncs/take_dryad_boots.ncs and b/_module/ncs/take_dryad_boots.ncs differ diff --git a/_module/ncs/talkquest_add1.ncs b/_module/ncs/talkquest_add1.ncs index 661d52a4..f9d7d562 100644 Binary files a/_module/ncs/talkquest_add1.ncs and b/_module/ncs/talkquest_add1.ncs differ diff --git a/_module/ncs/talkquest_add2.ncs b/_module/ncs/talkquest_add2.ncs index 16a74db0..5af6faef 100644 Binary files a/_module/ncs/talkquest_add2.ncs and b/_module/ncs/talkquest_add2.ncs differ diff --git a/_module/ncs/talkquest_add3.ncs b/_module/ncs/talkquest_add3.ncs index fd17e4af..046f1f96 100644 Binary files a/_module/ncs/talkquest_add3.ncs and b/_module/ncs/talkquest_add3.ncs differ diff --git a/_module/ncs/talkquest_add4.ncs b/_module/ncs/talkquest_add4.ncs index 4425fefb..95f960d0 100644 Binary files a/_module/ncs/talkquest_add4.ncs and b/_module/ncs/talkquest_add4.ncs differ diff --git a/_module/ncs/talkquest_add5.ncs b/_module/ncs/talkquest_add5.ncs index 206dde67..f3d76be6 100644 Binary files a/_module/ncs/talkquest_add5.ncs and b/_module/ncs/talkquest_add5.ncs differ diff --git a/_module/ncs/tbsnkeygen.ncs b/_module/ncs/tbsnkeygen.ncs index 9630683d..55f1989d 100644 Binary files a/_module/ncs/tbsnkeygen.ncs and b/_module/ncs/tbsnkeygen.ncs differ diff --git a/_module/ncs/timed_door.ncs b/_module/ncs/timed_door.ncs index 4edd4e12..78e66e1f 100644 Binary files a/_module/ncs/timed_door.ncs and b/_module/ncs/timed_door.ncs differ diff --git a/_module/ncs/tke_dragon_orb.ncs b/_module/ncs/tke_dragon_orb.ncs index 9d903083..22aaaa2c 100644 Binary files a/_module/ncs/tke_dragon_orb.ncs and b/_module/ncs/tke_dragon_orb.ncs differ diff --git a/_module/ncs/tmfkeygen.ncs b/_module/ncs/tmfkeygen.ncs index 075c6de1..d1e41989 100644 Binary files a/_module/ncs/tmfkeygen.ncs and b/_module/ncs/tmfkeygen.ncs differ diff --git a/_module/ncs/tracking.ncs b/_module/ncs/tracking.ncs index 9c9f755e..5a68518c 100644 Binary files a/_module/ncs/tracking.ncs and b/_module/ncs/tracking.ncs differ diff --git a/_module/ncs/tree_port.ncs b/_module/ncs/tree_port.ncs index ede8610b..efb2e3f7 100644 Binary files a/_module/ncs/tree_port.ncs and b/_module/ncs/tree_port.ncs differ diff --git a/_module/ncs/troll_nwn_spawn.ncs b/_module/ncs/troll_nwn_spawn.ncs index 31c623ac..f34950fb 100644 Binary files a/_module/ncs/troll_nwn_spawn.ncs and b/_module/ncs/troll_nwn_spawn.ncs differ diff --git a/_module/ncs/troll_spawn.ncs b/_module/ncs/troll_spawn.ncs index ba1e648d..76c97abc 100644 Binary files a/_module/ncs/troll_spawn.ncs and b/_module/ncs/troll_spawn.ncs differ diff --git a/_module/ncs/troll_userdefine.ncs b/_module/ncs/troll_userdefine.ncs index 70bedc4f..99a8a9ad 100644 Binary files a/_module/ncs/troll_userdefine.ncs and b/_module/ncs/troll_userdefine.ncs differ diff --git a/_module/ncs/tsskey.ncs b/_module/ncs/tsskey.ncs index f9652219..4c54bbdf 100644 Binary files a/_module/ncs/tsskey.ncs and b/_module/ncs/tsskey.ncs differ diff --git a/_module/ncs/tunnels_exit.ncs b/_module/ncs/tunnels_exit.ncs index 3db4e5dd..9edfdf1f 100644 Binary files a/_module/ncs/tunnels_exit.ncs and b/_module/ncs/tunnels_exit.ncs differ diff --git a/_module/ncs/ud_beamtarget.ncs b/_module/ncs/ud_beamtarget.ncs index 014340dc..b95d1d2e 100644 Binary files a/_module/ncs/ud_beamtarget.ncs and b/_module/ncs/ud_beamtarget.ncs differ diff --git a/_module/ncs/unequip_onenter2.ncs b/_module/ncs/unequip_onenter2.ncs index c201eca0..ad071b9c 100644 Binary files a/_module/ncs/unequip_onenter2.ncs and b/_module/ncs/unequip_onenter2.ncs differ diff --git a/_module/ncs/unlock_open_1.ncs b/_module/ncs/unlock_open_1.ncs index 497bcb12..2173f6be 100644 Binary files a/_module/ncs/unlock_open_1.ncs and b/_module/ncs/unlock_open_1.ncs differ diff --git a/_module/ncs/unlockforge.ncs b/_module/ncs/unlockforge.ncs index b6254f16..869918ed 100644 Binary files a/_module/ncs/unlockforge.ncs and b/_module/ncs/unlockforge.ncs differ diff --git a/_module/ncs/valen_attacks.ncs b/_module/ncs/valen_attacks.ncs index 32cd6d5c..8f4e852e 100644 Binary files a/_module/ncs/valen_attacks.ncs and b/_module/ncs/valen_attacks.ncs differ diff --git a/_module/ncs/vfxshadowarmor.ncs b/_module/ncs/vfxshadowarmor.ncs index 436a8d8e..8e65d8dc 100644 Binary files a/_module/ncs/vfxshadowarmor.ncs and b/_module/ncs/vfxshadowarmor.ncs differ diff --git a/_module/ncs/victors_helm_ck.ncs b/_module/ncs/victors_helm_ck.ncs index 11f711f2..6ecad8d5 100644 Binary files a/_module/ncs/victors_helm_ck.ncs and b/_module/ncs/victors_helm_ck.ncs differ diff --git a/_module/ncs/virgil_follow.ncs b/_module/ncs/virgil_follow.ncs index 14e10cfe..4e4e9bcf 100644 Binary files a/_module/ncs/virgil_follow.ncs and b/_module/ncs/virgil_follow.ncs differ diff --git a/_module/ncs/wasteland_tele.ncs b/_module/ncs/wasteland_tele.ncs index 4dd38322..825ee5df 100644 Binary files a/_module/ncs/wasteland_tele.ncs and b/_module/ncs/wasteland_tele.ncs differ diff --git a/_module/ncs/water_spawn.ncs b/_module/ncs/water_spawn.ncs index 997b4f22..bee6dc1a 100644 Binary files a/_module/ncs/water_spawn.ncs and b/_module/ncs/water_spawn.ncs differ diff --git a/_module/ncs/watervfx.ncs b/_module/ncs/watervfx.ncs index d6da81b5..45bac5cd 100644 Binary files a/_module/ncs/watervfx.ncs and b/_module/ncs/watervfx.ncs differ diff --git a/_module/ncs/wet_farm_ck.ncs b/_module/ncs/wet_farm_ck.ncs index 13477c16..aae58e66 100644 Binary files a/_module/ncs/wet_farm_ck.ncs and b/_module/ncs/wet_farm_ck.ncs differ diff --git a/_module/ncs/wet_farm_take.ncs b/_module/ncs/wet_farm_take.ncs index 03c15773..cffc3f31 100644 Binary files a/_module/ncs/wet_farm_take.ncs and b/_module/ncs/wet_farm_take.ncs differ diff --git a/_module/ncs/wet_farm_var_ck1.ncs b/_module/ncs/wet_farm_var_ck1.ncs index d3a3f319..f145d7b7 100644 Binary files a/_module/ncs/wet_farm_var_ck1.ncs and b/_module/ncs/wet_farm_var_ck1.ncs differ diff --git a/_module/ncs/wet_farm_var_ck2.ncs b/_module/ncs/wet_farm_var_ck2.ncs index 61073833..595bdb32 100644 Binary files a/_module/ncs/wet_farm_var_ck2.ncs and b/_module/ncs/wet_farm_var_ck2.ncs differ diff --git a/_module/ncs/wetland_guard.ncs b/_module/ncs/wetland_guard.ncs index a9802462..3ae96e41 100644 Binary files a/_module/ncs/wetland_guard.ncs and b/_module/ncs/wetland_guard.ncs differ diff --git a/_module/ncs/wetland_guard2.ncs b/_module/ncs/wetland_guard2.ncs index d7d319c9..1ddba5d4 100644 Binary files a/_module/ncs/wetland_guard2.ncs and b/_module/ncs/wetland_guard2.ncs differ diff --git a/_module/ncs/wings.ncs b/_module/ncs/wings.ncs index b4f59807..f9a30706 100644 Binary files a/_module/ncs/wings.ncs and b/_module/ncs/wings.ncs differ diff --git a/_module/ncs/witchammy_spawn.ncs b/_module/ncs/witchammy_spawn.ncs index 6edb1b3f..c23df1cd 100644 Binary files a/_module/ncs/witchammy_spawn.ncs and b/_module/ncs/witchammy_spawn.ncs differ diff --git a/_module/ncs/wkdkey.ncs b/_module/ncs/wkdkey.ncs index 712e12f8..e8b9c754 100644 Binary files a/_module/ncs/wkdkey.ncs and b/_module/ncs/wkdkey.ncs differ diff --git a/_module/ncs/wolfdoor1.ncs b/_module/ncs/wolfdoor1.ncs index 26af1218..a28fe5e7 100644 Binary files a/_module/ncs/wolfdoor1.ncs and b/_module/ncs/wolfdoor1.ncs differ diff --git a/_module/ncs/wolfdoor2.ncs b/_module/ncs/wolfdoor2.ncs index e76ae86d..4fb81bbe 100644 Binary files a/_module/ncs/wolfdoor2.ncs and b/_module/ncs/wolfdoor2.ncs differ diff --git a/_module/ncs/wolfdoor3.ncs b/_module/ncs/wolfdoor3.ncs index 28141599..2fa427dd 100644 Binary files a/_module/ncs/wolfdoor3.ncs and b/_module/ncs/wolfdoor3.ncs differ diff --git a/_module/ncs/wolfsha_get_int.ncs b/_module/ncs/wolfsha_get_int.ncs index 05e1ff47..fec11304 100644 Binary files a/_module/ncs/wolfsha_get_int.ncs and b/_module/ncs/wolfsha_get_int.ncs differ diff --git a/_module/ncs/wolftalk_get.ncs b/_module/ncs/wolftalk_get.ncs index fcf2e2f0..12c334b1 100644 Binary files a/_module/ncs/wolftalk_get.ncs and b/_module/ncs/wolftalk_get.ncs differ diff --git a/_module/ncs/wys_e_dummydmg.ncs b/_module/ncs/wys_e_dummydmg.ncs index 90782fef..66843314 100644 Binary files a/_module/ncs/wys_e_dummydmg.ncs and b/_module/ncs/wys_e_dummydmg.ncs differ diff --git a/_module/ncs/x0_ch_hen_block.ncs b/_module/ncs/x0_ch_hen_block.ncs new file mode 100644 index 00000000..77b29b8c Binary files /dev/null and b/_module/ncs/x0_ch_hen_block.ncs differ diff --git a/_module/ncs/x0_ch_hen_combat.ncs b/_module/ncs/x0_ch_hen_combat.ncs new file mode 100644 index 00000000..fcb92604 Binary files /dev/null and b/_module/ncs/x0_ch_hen_combat.ncs differ diff --git a/_module/ncs/x0_ch_hen_conv.ncs b/_module/ncs/x0_ch_hen_conv.ncs new file mode 100644 index 00000000..1d5c4b97 Binary files /dev/null and b/_module/ncs/x0_ch_hen_conv.ncs differ diff --git a/_module/ncs/x0_ch_hen_heart.ncs b/_module/ncs/x0_ch_hen_heart.ncs index 9f63ec77..4b2bf992 100644 Binary files a/_module/ncs/x0_ch_hen_heart.ncs and b/_module/ncs/x0_ch_hen_heart.ncs differ diff --git a/_module/ncs/x0_ch_hen_rest.ncs b/_module/ncs/x0_ch_hen_rest.ncs index f90b0e15..19d68e35 100644 Binary files a/_module/ncs/x0_ch_hen_rest.ncs and b/_module/ncs/x0_ch_hen_rest.ncs differ diff --git a/_module/ncs/x0_ch_hen_spawn.ncs b/_module/ncs/x0_ch_hen_spawn.ncs index b21514a4..da6a3b33 100644 Binary files a/_module/ncs/x0_ch_hen_spawn.ncs and b/_module/ncs/x0_ch_hen_spawn.ncs differ diff --git a/_module/ncs/x0_ch_hen_stealt.ncs b/_module/ncs/x0_ch_hen_stealt.ncs index f77e274f..dfb89170 100644 Binary files a/_module/ncs/x0_ch_hen_stealt.ncs and b/_module/ncs/x0_ch_hen_stealt.ncs differ diff --git a/_module/ncs/x0_o2_use_tdoor.ncs b/_module/ncs/x0_o2_use_tdoor.ncs index b8cec951..ca8c8694 100644 Binary files a/_module/ncs/x0_o2_use_tdoor.ncs and b/_module/ncs/x0_o2_use_tdoor.ncs differ diff --git a/_module/ncs/x2_def_immobile.ncs b/_module/ncs/x2_def_immobile.ncs index 41d2abde..2341a4a7 100644 Binary files a/_module/ncs/x2_def_immobile.ncs and b/_module/ncs/x2_def_immobile.ncs differ diff --git a/_module/ncs/x2_def_spawnwwp.ncs b/_module/ncs/x2_def_spawnwwp.ncs index 29546a85..4b62eb7b 100644 Binary files a/_module/ncs/x2_def_spawnwwp.ncs and b/_module/ncs/x2_def_spawnwwp.ncs differ diff --git a/_module/ncs/x2_mod_def_equ.ncs b/_module/ncs/x2_mod_def_equ.ncs index 89c91665..ed33c865 100644 Binary files a/_module/ncs/x2_mod_def_equ.ncs and b/_module/ncs/x2_mod_def_equ.ncs differ diff --git a/_module/ncs/x2_mod_def_load.ncs b/_module/ncs/x2_mod_def_load.ncs index 30c0f235..d5ca05fa 100644 Binary files a/_module/ncs/x2_mod_def_load.ncs and b/_module/ncs/x2_mod_def_load.ncs differ diff --git a/_module/ncs/x2_mod_def_unequ.ncs b/_module/ncs/x2_mod_def_unequ.ncs index 74e7cbc8..46427df6 100644 Binary files a/_module/ncs/x2_mod_def_unequ.ncs and b/_module/ncs/x2_mod_def_unequ.ncs differ diff --git a/_module/ncs/xov_hen_fired.ncs b/_module/ncs/xov_hen_fired.ncs index df94b7d9..71df57c5 100644 Binary files a/_module/ncs/xov_hen_fired.ncs and b/_module/ncs/xov_hen_fired.ncs differ diff --git a/_module/ncs/xov_hen_join.ncs b/_module/ncs/xov_hen_join.ncs index d9ccacc0..37d1136e 100644 Binary files a/_module/ncs/xov_hen_join.ncs and b/_module/ncs/xov_hen_join.ncs differ diff --git a/_module/ncs/xp_disarm.ncs b/_module/ncs/xp_disarm.ncs index 541f22ce..ddbcf8fc 100644 Binary files a/_module/ncs/xp_disarm.ncs and b/_module/ncs/xp_disarm.ncs differ diff --git a/_module/ncs/xp_unlock.ncs b/_module/ncs/xp_unlock.ncs index 040f84a5..e60208a5 100644 Binary files a/_module/ncs/xp_unlock.ncs and b/_module/ncs/xp_unlock.ncs differ diff --git a/_module/ncs/xs_catapult_use.ncs b/_module/ncs/xs_catapult_use.ncs index be58445d..d365ed5f 100644 Binary files a/_module/ncs/xs_catapult_use.ncs and b/_module/ncs/xs_catapult_use.ncs differ diff --git a/_module/ncs/yang_death.ncs b/_module/ncs/yang_death.ncs index 5a09b5d7..ff1b8566 100644 Binary files a/_module/ncs/yang_death.ncs and b/_module/ncs/yang_death.ncs differ diff --git a/_module/ncs/yin_death.ncs b/_module/ncs/yin_death.ncs index 5f0c68f5..22225a48 100644 Binary files a/_module/ncs/yin_death.ncs and b/_module/ncs/yin_death.ncs differ diff --git a/_module/ncs/yingyangcrystal.ncs b/_module/ncs/yingyangcrystal.ncs index e3376d06..dd583a28 100644 Binary files a/_module/ncs/yingyangcrystal.ncs and b/_module/ncs/yingyangcrystal.ncs differ diff --git a/_module/ncs/yinyang_exit.ncs b/_module/ncs/yinyang_exit.ncs index 8dd84fd7..d0a1d622 100644 Binary files a/_module/ncs/yinyang_exit.ncs and b/_module/ncs/yinyang_exit.ncs differ diff --git a/_module/ncs/yinyang_ondeath.ncs b/_module/ncs/yinyang_ondeath.ncs index 909212d3..1daf9521 100644 Binary files a/_module/ncs/yinyang_ondeath.ncs and b/_module/ncs/yinyang_ondeath.ncs differ diff --git a/_module/ncs/yinyang_spawn.ncs b/_module/ncs/yinyang_spawn.ncs index e50a0347..e25f1eb2 100644 Binary files a/_module/ncs/yinyang_spawn.ncs and b/_module/ncs/yinyang_spawn.ncs differ diff --git a/_module/ncs/zep_openclose.ncs b/_module/ncs/zep_openclose.ncs index 7f494598..130172c9 100644 Binary files a/_module/ncs/zep_openclose.ncs and b/_module/ncs/zep_openclose.ncs differ diff --git a/_module/nss/0c_assoc_actions.nss b/_module/nss/0c_assoc_actions.nss new file mode 100644 index 00000000..74975d45 --- /dev/null +++ b/_module/nss/0c_assoc_actions.nss @@ -0,0 +1,172 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_assoc_actions + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Conversation script that sets modes or allows oAssociate to do actions from a + conversation. + Param "sAction" +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oPC = GetPCSpeaker(); + object oAssociate = OBJECT_SELF; + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + object oArea = GetArea(oAssociate); + string sAction = GetScriptParam("sAction"); + // 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); + } + else if(sAction == "BasicTactics") + { + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, ""); + ai_SetAssociateAIScript(oAssociate, FALSE); + } + else if(sAction == "AmbushTactics") + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, "ai_a_ambusher"); + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, "ai_a_ambusher"); + } + else if(sAction == "DefensiveTactics") + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, "ai_a_defensive"); + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, "ai_a_defensive"); + } + else if(sAction == "RangedTactics") + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, "ai_a_ranged"); + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, "ai_a_ranged"); + } + else if(sAction == "Taunt") + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, "ai_a_taunter"); + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, "ai_a_taunter"); + } + else if(sAction == "CounterSpell") + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, "ai_a_cntrspell"); + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, "ai_a_cntrspell"); + } + else if(sAction == "PeaceTactics") + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, "ai_a_peaceful"); + } + else if(sAction == "AttackTactics") + { + if(ai_GetAIMode(oAssociate, AI_MODE_CHECK_ATTACK)) + { + ai_SetAIMode(oAssociate, AI_MODE_CHECK_ATTACK, FALSE); + } + else ai_SetAIMode(oAssociate, AI_MODE_CHECK_ATTACK, TRUE); + } + else if(sAction == "FollowCloser") ai_FollowIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sAction == "FollowFarther") ai_FollowIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sAction == "Pickup") ai_Loot(oPC, oAssociate, sAssociateType); + else if(sAction == "HealSelf") ai_Heal_OnOff(oPC, oAssociate, sAssociateType, 1); + else if(sAction == "HealAllies") ai_Heal_OnOff(oPC, oAssociate, sAssociateType, 2); + else if(sAction == "HealOutMinus") ai_Heal_Button(oPC, oAssociate, -5, AI_HEAL_OUT_OF_COMBAT_LIMIT, sAssociateType); + else if(sAction == "HealOutPlus") ai_Heal_Button(oPC, oAssociate, 5, AI_HEAL_OUT_OF_COMBAT_LIMIT, sAssociateType); + else if(sAction == "HealInMinus") ai_Heal_Button(oPC, oAssociate, -5, AI_HEAL_IN_COMBAT_LIMIT, sAssociateType); + else if(sAction == "HealInPlus") ai_Heal_Button(oPC, oAssociate, 5, AI_HEAL_IN_COMBAT_LIMIT, sAssociateType); + else if(sAction == "Traps") ai_Traps(oPC, oAssociate, sAssociateType); + else if(sAction == "Locks") ai_Locks(oPC, oAssociate, sAssociateType, 1); + else if(sAction == "Bash") ai_Locks(oPC, oAssociate, sAssociateType, 2); + else if(sAction == "Search") ai_Search(oPC, oAssociate, sAssociateType); + else if(sAction == "Stealth") ai_Stealth(oPC, oAssociate, sAssociateType); + else if(sAction == "NoMagic") ai_UseMagic(oPC, oAssociate, sAssociateType); + else if(sAction == "DefensiveCasting") ai_UseOffensiveMagic(oPC, oAssociate, TRUE, FALSE, sAssociateType); + else if(sAction == "OffensiveCasting") ai_UseOffensiveMagic(oPC, oAssociate, FALSE, TRUE, sAssociateType); + else if(sAction == "MagicMinus") ai_MagicIncrement(oPC, oAssociate, -1, sAssociateType); + else if(sAction == "MagicPlus") ai_MagicIncrement(oPC, oAssociate, 1, sAssociateType); + else if(sAction == "Speaking") + { + if(ai_GetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK)) + { + ai_SetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK, FALSE); + } + else ai_SetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK, TRUE); + } + else if(sAction == "Ranged") + { + if(ai_GetAIMode(oAssociate, AI_MODE_STOP_RANGED)) + { + ai_SetAIMode(oAssociate, AI_MODE_STOP_RANGED, FALSE); + } + else ai_SetAIMode(oAssociate, AI_MODE_STOP_RANGED, TRUE); + } + else if(sAction == "AtkAssociates") + { + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES)) + { + ai_SetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES, FALSE); + } + else ai_SetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES, TRUE); + } + else if(sAction == "BuffFirst") + { + if(ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_MASTER)) + { + ai_SetMagicMode(oAssociate, AI_MAGIC_BUFF_MASTER, FALSE); + } + else ai_SetMagicMode(oAssociate, AI_MAGIC_BUFF_MASTER, TRUE); + } + else if(sAction == "RestBuffing") + { + if(ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST)) + { + ai_SetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST, FALSE); + } + else ai_SetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST, TRUE); + } + else if(sAction == "Dispel") + { + if(ai_GetMagicMode(oAssociate, AI_MAGIC_STOP_DISPEL)) + { + ai_SetMagicMode(oAssociate, AI_MAGIC_STOP_DISPEL, FALSE); + } + else ai_SetMagicMode(oAssociate, AI_MAGIC_STOP_DISPEL, TRUE); + } + else if(sAction == "MagicItems") + { + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS)) + { + ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS, FALSE); + } + else ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS, TRUE); + } + else if(sAction == "Identify") + { + ai_IdentifyAllVsKnowledge(oAssociate, oPC, oPC); + return; + } + else if(sAction == "GiveUnIdentifiedItems") + { + ai_ClearCreatureActions(); + object oItem = GetFirstItemInInventory(oAssociate); + while(oItem != OBJECT_INVALID) + { + if(!GetIdentified(oItem)) ActionGiveItem(oItem, oPC); + oItem = GetNextItemInInventory(oAssociate); + } + return; + } + else if(sAction == "GiveMagicItems") + { + ai_ClearCreatureActions(); + itemproperty ipItemProp; + object oItem = GetFirstItemInInventory(oAssociate); + while(oItem != OBJECT_INVALID) + { + ipItemProp = GetFirstItemProperty(oItem); + if(GetIsItemPropertyValid(ipItemProp)) ActionGiveItem(oItem, oPC); + oItem = GetNextItemInInventory(oAssociate); + } + return; + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} diff --git a/_module/nss/0c_cast_polymorp.nss b/_module/nss/0c_cast_polymorp.nss new file mode 100644 index 00000000..b06f1834 --- /dev/null +++ b/_module/nss/0c_cast_polymorp.nss @@ -0,0 +1,18 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// + Script Name: 0c_cast_polymorp + Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Conversation script to have a henchman cast a polymorph spell. + int nSpell is the spell to cast. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_items" +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/_module/nss/0c_fire_henchmen.nss b/_module/nss/0c_fire_henchmen.nss new file mode 100644 index 00000000..5505be97 --- /dev/null +++ b/_module/nss/0c_fire_henchmen.nss @@ -0,0 +1,15 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_fire_henchmen + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Action taken script to fire/remove henchman for higher. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oHenchman = OBJECT_SELF; + ai_ClearCreatureActions(); + ai_FireHenchman (GetPCSpeaker(), oHenchman); + PlayVoiceChat (VOICE_CHAT_GOODBYE, oHenchman); +} + diff --git a/_module/nss/0c_get_convo.nss b/_module/nss/0c_get_convo.nss new file mode 100644 index 00000000..7cdfd409 --- /dev/null +++ b/_module/nss/0c_get_convo.nss @@ -0,0 +1,22 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_get_convo + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Action taken script that leaves the current conversation and starts a new + conversation with oCreature using the linked conversation instead of the + ai_Henchman conversation. + + Allows use of ai_conversation for henchman in other modules. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +void BeginOriginalHenchmanConversation(string sDialog, object oPC) +{ + BeginConversation(sDialog, oPC); +} +void main() +{ + ai_ClearCreatureActions(); + // Need to check special dialogs for HOTU henchman. + string sDialog = GetDialogFileToUse(GetLastSpeaker()); + DelayCommand(0.0, BeginOriginalHenchmanConversation(sDialog, GetPCSpeaker())); +} diff --git a/_module/nss/0c_get_henchman.nss b/_module/nss/0c_get_henchman.nss new file mode 100644 index 00000000..e0952d57 --- /dev/null +++ b/_module/nss/0c_get_henchman.nss @@ -0,0 +1,25 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_get_henchman + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Action taken script that adds oCreature to oPC's party as a henchman + while giving a random message. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + object oPC = GetPCSpeaker(); + AddHenchman(oPC, oCreature); + int nVoice; + switch(d4()) + { + case 1: nVoice = VOICE_CHAT_CANDO; break; + case 2: nVoice = VOICE_CHAT_CHEER; break; + case 3: nVoice = VOICE_CHAT_GOODIDEA; break; + case 4: nVoice = VOICE_CHAT_LAUGH; break; + } + PlayVoiceChat(nVoice, oCreature); +} + + diff --git a/_module/nss/0c_h_cast_spell.nss b/_module/nss/0c_h_cast_spell.nss new file mode 100644 index 00000000..40868d87 --- /dev/null +++ b/_module/nss/0c_h_cast_spell.nss @@ -0,0 +1,12 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_cast_spell + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Action taken script that sets the specified spell to be cast. + Param + nSpell - the spell to cast. +*/////////////////////////////////////////////////////////////////////////////// +void main() +{ + SetLocalInt (OBJECT_SELF, "0_SPELL_TO_CAST", StringToInt (GetScriptParam ("nSpell"))); +} diff --git a/_module/nss/0c_henchmenspell.nss b/_module/nss/0c_henchmenspell.nss new file mode 100644 index 00000000..5e64cbac --- /dev/null +++ b/_module/nss/0c_henchmenspell.nss @@ -0,0 +1,81 @@ +/*/////////////////////////////////////////////////////////////////////////////// + Script: 0c_henchmenspell + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Action script to cast a specific spell for a henchman. + + Script Param + 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. + nBuffType = 1 all 2 short 3 long, 4 healing, 5 lay on hands. + If nBuffType is 0 then it will cast a specific spell from + Variable "0_SPELL_TO_CAST". Use script: 0c_h_spell_cast spell to set the spell. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +float ai_UseLayOnHands(object oTarget, object oPC, float fDelay, object oCaster); +void main() +{ + object oTarget, oPC = GetPCSpeaker(); + object oCreature = OBJECT_SELF; + float fDelay; + int nTarget = StringToInt(GetScriptParam("nTarget")); + int nBuffType = StringToInt(GetScriptParam("nBuffType")); + // Cast a group of buff spells based on nBuffType and nTarget or a single spell. + if(nBuffType < 4) + { + // Cast a specific spell. + if(nBuffType == 0) + { + int nSpell = GetLocalInt(oCreature, "0_SPELL_TO_CAST"); + // These are buff spells so Acid fog (index 0) is not a valid spell. + if(nSpell > 0) + { + ai_ClearCreatureActions(); + object oTarget = GetLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nTarget)); + if(oTarget != OBJECT_INVALID && ai_CheckAndCastSpell(oCreature, nSpell, 0, 0.0f, oTarget, oPC)) + { + DeleteLocalInt(oCreature, "0_SPELL_TO_CAST"); + } + else + { + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) PlayVoiceChat(VOICE_CHAT_CANTDO, oCreature); + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + ai_SendMessages("I cannot cast " + sSpellName + ".", AI_COLOR_RED, oPC); + } + } + } + // Cast a creatures buff spells on nTarget. + else ai_CastBuffs(oCreature, nBuffType, nTarget, oPC); + } + // Cast Healing spells. + else if(nBuffType == 4) + { + ai_SetupAllyTargets(oCreature, oPC); + oTarget = GetLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nTarget)); + ai_TryHealing(oCreature, oTarget); + } + // Use lay on hands. + else if(nBuffType == 5) + { + ai_SetupAllyTargets(oCreature, oPC); + oTarget = GetLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nTarget)); + ai_UseLayOnHands(oTarget, oPC, 0.0f, oCreature); + } + else if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) PlayVoiceChat(VOICE_CHAT_CUSS, oCreature); +} +float ai_UseLayOnHands(object oTarget, object oPC, float fDelay, object oCreature) +{ + int nHpLost = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); + if(!nHpLost) + { + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) PlayVoiceChat(VOICE_CHAT_CANTDO, oCreature); + ai_SendMessages(GetName(oTarget) + " does not need healed.", AI_COLOR_RED, oPC); + } + else + { + ai_SendMessages(GetName(oCreature) + " is laying hands on " + GetName(oTarget), AI_COLOR_GREEN, oPC); + ActionUseFeat(FEAT_LAY_ON_HANDS, oTarget); + fDelay += 6.0f; + } + return fDelay; +} diff --git a/_module/nss/0c_if_a_magic_m.nss b/_module/nss/0c_if_a_magic_m.nss new file mode 100644 index 00000000..7ef48321 --- /dev/null +++ b/_module/nss/0c_if_a_magic_m.nss @@ -0,0 +1,16 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_a_magic_m + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if the henchmen has a specific + associate magic mode. + Param: + nMode - The mode to check. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + int nMode = StringToInt(GetScriptParam("nMode")); + return ai_GetMagicMode (oHenchman, nMode); +} diff --git a/_module/nss/0c_if_ass_convo.nss b/_module/nss/0c_if_ass_convo.nss new file mode 100644 index 00000000..190a0f73 --- /dev/null +++ b/_module/nss/0c_if_ass_convo.nss @@ -0,0 +1,132 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_ass_convo + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that has the henchman tell the player what options + have been selected. + + sOption will decide what the henchman says. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + object oPC = GetPCSpeaker(); + object oAssociate = OBJECT_SELF; + string sParam = GetScriptParam("sOption"); + if(sParam == "BaseMode") + { + string sBaseMode = "I'm ready to attack."; + string sVolume = " While shouting when I see things."; + // Lets get which base mode the henchman is in. + if(ai_GetAIMode(oAssociate, AI_MODE_STAND_GROUND)) sBaseMode = "I'm holding here."; + else if(ai_GetAIMode(oAssociate, AI_MODE_DEFEND_MASTER)) sBaseMode = "I'm defending you."; + else if(ai_GetAIMode(oAssociate, AI_MODE_FOLLOW)) sBaseMode = "I'm following you."; + if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_a_peaceful") sBaseMode = "I will not fight the enemy!"; + if(ai_GetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK)) sVolume = " While not speaking unless spoken to."; + SetCustomToken(AI_BASE_CUSTOM_TOKEN, sBaseMode + sVolume); + } + else if(sParam == "CombatTactics") + { + string sRangedUse = "", sCombatTactic = "I'm using my best judgement in combat "; + string sAtkAssociates = ""; + string sTargets = "against all enemies and "; + // Lets get which base mode the henchman is in. + if(ai_GetAIMode(oAssociate, AI_MODE_CHECK_ATTACK)) sTargets = "against enemies I can handle and "; + if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_a_ambusher") sCombatTactic = "I'm using ambush tactics "; + else if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_a_defensive") sCombatTactic = "I'm using defensive tactics "; + else if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_a_taunter") sCombatTactic = "I'm ready to taunt "; + else if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_a_cntrspell") sCombatTactic = "I'm ready to counter spell "; + if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_a_peaceful") + { + sCombatTactic = "I will not fight the enemy!"; + sTargets = ""; + } + else + { + if(ai_GetAIMode(oAssociate, AI_MODE_STOP_RANGED)) sRangedUse = "will not use a ranged weapon."; + else sRangedUse = "will use a ranged weapon."; + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES)) sAtkAssociates = " I will also ignore familiars, companions, and summons."; + else sAtkAssociates = " I will also attack familiars, companions, and summons."; + } + SetCustomToken(AI_BASE_CUSTOM_TOKEN + 1, sCombatTactic + sTargets + sRangedUse + sAtkAssociates); + } + else if(sParam == "Plans") + { + float fFollowRange = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE); + string sFollowRange = FloatToString(fFollowRange, 0, 0); + string sDistance = "I'm following from " + sFollowRange + " meters away while"; + string sStealth, sSearch, sPickup; + if(ai_GetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS)) sPickup = " picking up items"; + else sPickup = " not picking up any items"; + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH)) sStealth = " in stealth"; + else sStealth = ""; + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH)) sSearch = " and searching"; + else sSearch = ""; + SetCustomToken(AI_BASE_CUSTOM_TOKEN + 2, sDistance + sPickup + sStealth + sSearch + "."); + } + else if(sParam == "Healing") + { + 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 " + + sHealingIn + " health in combat and " + sHealingOut + " out of combat."); + } + else if(sParam == "Spells") + { + string sCastingLevel = "[" + IntToString(GetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT)) + "] "; + string sCasting = "I'm casting"; + string sType = " spells I choose."; + string sBuff = " I'll also targeting anyone that needs it "; + string sDispel = "while using Dispel spells."; + string sMagicItems = " Lastly I'll use any magic items I have."; + if(ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_MASTER)) sBuff = " Ofcourse I'll target you first "; + if(ai_GetMagicMode(oAssociate, AI_MAGIC_STOP_DISPEL)) sDispel = "while not using Dispel spells."; + if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_a_cntrspell") + { + sCasting = "I'm ready to counter spell our enemies."; + sType = ""; + sBuff = ""; + sDispel = ""; + } + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC)) + { + sCasting = "I'm not use any magic."; + sType = ""; + sBuff = ""; + sDispel = ""; + } + else if(ai_GetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING)) sType = " defensive spells only."; + else if(ai_GetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING)) + { + sType = " offensive spells only."; + sBuff = ""; + } + else if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS)) sMagicItems = " Finally I'll not use magic items."; + SetCustomToken(AI_BASE_CUSTOM_TOKEN + 5, sCastingLevel + sCasting + sType + sBuff + sDispel+ sMagicItems); + } + else if(sParam == "Objects") + { + int bTraps = ai_GetAIMode(oAssociate, AI_MODE_DISARM_TRAPS); + int bLocks = ai_GetAIMode(oAssociate, AI_MODE_PICK_LOCKS); + int bBash = ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS); + string sText = "I'm going to ignore all traps and locks."; + if(bTraps && bLocks && bBash) + { + sText = "I'm disarming all the traps and am either picking or bashing any of the locks we find."; + } + else if(bTraps && bLocks) sText = "I'm going to disarm all the traps and I'll pick all the locks we encounter."; + else if(bTraps && bBash) sText = "I shall disarm all the traps and will bash any locks we come across."; + else if(bTraps) sText = "I will disarm all the traps I can but will leave any locks for you to deal with."; + else if(bLocks && bBash) sText = "I will leave the traps for you but will either pick or bash any locks we see."; + else if(bLocks) sText = "I'll keep my distance from any traps we see, but will pick the locks found."; + else if(bBash) sText = "I'll let you mess with the traps, but I'll bash any locks that are out there."; + SetCustomToken(AI_BASE_CUSTOM_TOKEN + 3, sText); + } + else if(sParam == "RestBuffing") + { + string sRestBuffing = ""; + if(!ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST)) sRestBuffing = "not "; + SetCustomToken(AI_BASE_CUSTOM_TOKEN + 10, "After we rest I am " + sRestBuffing + "casting my long buff spells on us."); + } + return TRUE; +} diff --git a/_module/nss/0c_if_assoc_mode.nss b/_module/nss/0c_if_assoc_mode.nss new file mode 100644 index 00000000..342bfff3 --- /dev/null +++ b/_module/nss/0c_if_assoc_mode.nss @@ -0,0 +1,22 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_assoc_mode + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if the henchmen has a specific + associate mode. + Param: + nMode - The mode to check. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + int nMode = StringToInt(GetScriptParam("nMode")); + // This conversation line turns off picking up any items. + if (nMode == -1) + { + if(ai_SetAIMode (oHenchman, AI_MODE_PICKUP_ITEMS)) return TRUE; + return FALSE; + } + return ai_GetAIMode (oHenchman, nMode); +} diff --git a/_module/nss/0c_if_cntrspell.nss b/_module/nss/0c_if_cntrspell.nss new file mode 100644 index 00000000..a0a5c878 --- /dev/null +++ b/_module/nss/0c_if_cntrspell.nss @@ -0,0 +1,17 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_cntrspell + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that returns TRUE the server allows a henchman to + use counterspell and if they don't have the counterspell ai script set. + Param: + sAIScript - The special combat script to check. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + return (AI_COUNTERSPELLING_ON && + ai_CheckClassType(oHenchman, AI_CLASS_TYPE_CASTER) && + GetLocalString(oHenchman, AI_COMBAT_SCRIPT) != "ai_a_cntrspell"); +} diff --git a/_module/nss/0c_if_com_script.nss b/_module/nss/0c_if_com_script.nss new file mode 100644 index 00000000..34d8370e --- /dev/null +++ b/_module/nss/0c_if_com_script.nss @@ -0,0 +1,16 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_com_script + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that returns TRUE the caller does have an ai combat + script set to sAIScript. + Param: + sAIScript - The special combat script to check. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + string sAIScript = GetScriptParam("sAIScript"); + string sAICombatScript = GetLocalString (OBJECT_SELF, AI_COMBAT_SCRIPT); + return (sAIScript == sAICombatScript); +} diff --git a/_module/nss/0c_if_convo.nss b/_module/nss/0c_if_convo.nss new file mode 100644 index 00000000..83e1db43 --- /dev/null +++ b/_module/nss/0c_if_convo.nss @@ -0,0 +1,21 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_if_convo + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that check if oCreature has a linked conversation. + Only checks for Henchman. + Allows use of ai_conversation for henchman in other modules. +*/////////////////////////////////////////////////////////////////////////////// +#include "nw_inc_gff" +#include "0i_messages" +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + if(GetAssociateType(oHenchman) == ASSOCIATE_TYPE_HENCHMAN) + { + json jHenchman = ObjectToJson(oHenchman); + string sConversation = JsonGetString(GffGetResRef(jHenchman, "Conversation")); + if(sConversation != "") return TRUE; + } + return FALSE; +} diff --git a/_module/nss/0c_if_has_assoc.nss b/_module/nss/0c_if_has_assoc.nss new file mode 100644 index 00000000..cd16680b --- /dev/null +++ b/_module/nss/0c_if_has_assoc.nss @@ -0,0 +1,18 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_has_assoc + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if caller has the specified feat + to summon either a companion or a familiar and they are not summoned. + Param + sAssociate - "Familiar" or "Companion" +*/////////////////////////////////////////////////////////////////////////////// +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + string sAssociate = GetScriptParam("sAssociate"); + if(sAssociate == "Familiar" && GetHasFeat(FEAT_SUMMON_FAMILIAR, oHenchman) && + GetAssociate(ASSOCIATE_TYPE_FAMILIAR) == OBJECT_INVALID) return TRUE; + return (sAssociate == "Companion" && GetHasFeat(FEAT_ANIMAL_COMPANION, oHenchman) && + GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION) == OBJECT_INVALID); +} diff --git a/_module/nss/0c_if_has_class.nss b/_module/nss/0c_if_has_class.nss new file mode 100644 index 00000000..1251c0a5 --- /dev/null +++ b/_module/nss/0c_if_has_class.nss @@ -0,0 +1,28 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_has_class + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if conversation owner has a + specified class. Multiple classes maybe selected. + Param + nClass# - the class to look for use nClass1, nClass2, nClass3 for each one to check. +*/////////////////////////////////////////////////////////////////////////////// +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + int nCntr = 1; + int nClass; + string sClass; + while(nCntr < 10) + { + sClass = GetScriptParam("nClass" + IntToString(nCntr)); + if(sClass != "") + { + nClass = StringToInt(sClass); + if(GetLevelByClass(nClass, oHenchman)) return TRUE; + nCntr++; + } + else break; + } + return FALSE; +} diff --git a/_module/nss/0c_if_has_feat.nss b/_module/nss/0c_if_has_feat.nss new file mode 100644 index 00000000..fc111d84 --- /dev/null +++ b/_module/nss/0c_if_has_feat.nss @@ -0,0 +1,22 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_has_feat + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if they have a specific feat. + Param: + sTarget - either "OBJECT_SELF", or "PCSpeaker", blanks defaults to "PCSpeaker" + nFeat - the feat number from Feats.2da + bNot - if 1 TRUE then this returns true for the target not having the feat. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_main" +int StartingConditional() +{ + string sTarget = GetScriptParam("sTarget"); + int nFeat = StringToInt(GetScriptParam("nFeat")); + int bNot = StringToInt(GetScriptParam("bNot")); + object oCreature; + if(sTarget == "OBJECT_SELF") oCreature = OBJECT_SELF; + else if(sTarget == "" || sTarget == "PCSpeaker") oCreature = GetPCSpeaker(); + if(bNot) return !GetHasFeat(nFeat, oCreature); + return (GetHasFeat(nFeat ,oCreature) || ai_GetIsDungeonMaster(oCreature)); +} diff --git a/_module/nss/0c_if_has_spell.nss b/_module/nss/0c_if_has_spell.nss new file mode 100644 index 00000000..02004ff1 --- /dev/null +++ b/_module/nss/0c_if_has_spell.nss @@ -0,0 +1,26 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_has_spell + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if caster can cast the specified spell. + Param + nSpell# - the spell to look for nSpell1, sSpell2, nSpell3 for each spell to check. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_spells" +int StartingConditional() +{ + object oCaster = OBJECT_SELF; + int nCnt = 1; + int nSpell; + string sSpell; + while(nCnt < 20) + { + sSpell = GetScriptParam("nSpell" + IntToString(nCnt)); + if(sSpell == "") return FALSE; + nSpell = StringToInt(sSpell); + if(GetHasSpell(nSpell, oCaster)) return TRUE; + //else if(ai_GetKnownSpell(oCaster, nSpell)) return TRUE; + nCnt++; + } + return FALSE; +} diff --git a/_module/nss/0c_if_hen_leave.nss b/_module/nss/0c_if_hen_leave.nss new file mode 100644 index 00000000..e2b2bbdc --- /dev/null +++ b/_module/nss/0c_if_hen_leave.nss @@ -0,0 +1,12 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_hen_leave + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that check if allowing the player to remove a henchman + is activated on this server. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + return AI_REMOVE_HENCHMAN_ON; +} diff --git a/_module/nss/0c_if_identify.nss b/_module/nss/0c_if_identify.nss new file mode 100644 index 00000000..b593bafd --- /dev/null +++ b/_module/nss/0c_if_identify.nss @@ -0,0 +1,17 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_identify + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if the henchmen has a better lore + skill than the speaker. + Also checks AI_IDENTIFY_ON to see if the server wants them to help. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + if (!AI_IDENTIFY_ON && !ai_CanISpeak (oHenchman)) return FALSE; + int nHenchmanLore = GetSkillRank(SKILL_LORE, oHenchman); + int nMasterLore = GetSkillRank(SKILL_LORE, GetMaster(oHenchman)); + return (nHenchmanLore > nMasterLore); +} diff --git a/_module/nss/0c_if_not_master.nss b/_module/nss/0c_if_not_master.nss new file mode 100644 index 00000000..73cb84e6 --- /dev/null +++ b/_module/nss/0c_if_not_master.nss @@ -0,0 +1,11 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_not_master + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks if the speaker is the master of this + henchman. +*/////////////////////////////////////////////////////////////////////////////// +int StartingConditional() +{ + return !GetIsObjectValid(GetMaster()); +} diff --git a/_module/nss/0c_if_open_inven.nss b/_module/nss/0c_if_open_inven.nss new file mode 100644 index 00000000..2755c990 --- /dev/null +++ b/_module/nss/0c_if_open_inven.nss @@ -0,0 +1,13 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_open_equip + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks if opening a henchmans inventory + is activated on this server. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + if(GetAssociateType(OBJECT_SELF) != ASSOCIATE_TYPE_HENCHMAN) return FALSE; + return AI_OPEN_INVENTORY; +} diff --git a/_module/nss/0c_if_pickuploot.nss b/_module/nss/0c_if_pickuploot.nss new file mode 100644 index 00000000..f4edfdb3 --- /dev/null +++ b/_module/nss/0c_if_pickuploot.nss @@ -0,0 +1,12 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_pickuploot + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that check if having associates picking up loot is + activated on this server. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + return AI_PICKUP_LOOT; +} diff --git a/_module/nss/0c_if_polymorph.nss b/_module/nss/0c_if_polymorph.nss new file mode 100644 index 00000000..e2317f6f --- /dev/null +++ b/_module/nss/0c_if_polymorph.nss @@ -0,0 +1,11 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_polymorph +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if the caller is polymorphed. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + if (GetLocalInt(OBJECT_SELF, AI_NORMAL_FORM) != 0) return TRUE; + return FALSE; +} diff --git a/_module/nss/0c_if_scout.nss b/_module/nss/0c_if_scout.nss new file mode 100644 index 00000000..9f7b0a3b --- /dev/null +++ b/_module/nss/0c_if_scout.nss @@ -0,0 +1,11 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_scout + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that check if scouting is activated on this server. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + return AI_SCOUT_AHEAD_ON; +} diff --git a/_module/nss/0c_if_skillrank.nss b/_module/nss/0c_if_skillrank.nss new file mode 100644 index 00000000..e0d77294 --- /dev/null +++ b/_module/nss/0c_if_skillrank.nss @@ -0,0 +1,18 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_SkillRank + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that checks to see if the caller's skill ranks + are above or equal to the param value. + Param: + nSkill - the skill number for the skill. See skills.2da. + nRank - the rank required. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_main" +int StartingConditional() +{ + string sSkill = GetScriptParam("nSkill"); + if(sSkill == "") return FALSE; + int nRank = StringToInt(GetScriptParam("nRank")); + return (GetSkillRank(StringToInt(sSkill)) >= nRank); +} diff --git a/_module/nss/0c_if_taunt.nss b/_module/nss/0c_if_taunt.nss new file mode 100644 index 00000000..3e0fde64 --- /dev/null +++ b/_module/nss/0c_if_taunt.nss @@ -0,0 +1,15 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_if_taunt + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that returns TRUE the server allows a henchman to + taunt and if they have the don't have the taunt ai script set. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + object oHenchman = OBJECT_SELF; + return (AI_TAUNTING_ON && + GetSkillRank(SKILL_TAUNT, oHenchman) > ai_GetCharacterLevels(oHenchman) && + GetLocalString(oHenchman, AI_COMBAT_SCRIPT) != "ai_a_taunter"); +} diff --git a/_module/nss/0c_listhenchman.nss b/_module/nss/0c_listhenchman.nss new file mode 100644 index 00000000..802f1003 --- /dev/null +++ b/_module/nss/0c_listhenchman.nss @@ -0,0 +1,19 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// + Script Name: 0c_cast_polymorp + Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Conversation script to setup the tokens for the henchman in the speakers party + except for who they are talking to. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +void main() +{ + object oSpeaker = OBJECT_SELF; + object oPC = GetPCSpeaker(); + int nCntr = 1; + object oHenchman = GetHenchman(oPC, nCntr); + while(oHenchman != OBJECT_INVALID) + { + if(oHenchman != oSpeaker) SetCustomToken(77100 + nCntr, GetName(oHenchman)); + oHenchman = GetHenchman(oPC, ++nCntr); + } +} diff --git a/_module/nss/0c_no_com_script.nss b/_module/nss/0c_no_com_script.nss new file mode 100644 index 00000000..ca5c3863 --- /dev/null +++ b/_module/nss/0c_no_com_script.nss @@ -0,0 +1,27 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0c_no_com_script + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Text Appears When script that returns TRUE the caller does not have an ai combat + script set to sAIScript. + if sAIScript is blank then if its equal to all of them. + Param: sAIScripts:"ai_a_ambusher", "ai_a_defensive", "ai_a_taunter", "ai_coward". + sAIScript - The special combat script to check. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +int StartingConditional() +{ + string sAIScript = GetScriptParam("sAIScript"); + string sAICombatScript = GetLocalString (OBJECT_SELF, AI_COMBAT_SCRIPT); + // This is the value for do your own thing in combat! + if (sAIScript == "") + { + return (sAICombatScript == "ai_a_ambusher" || + sAICombatScript == "ai_a_defensive" || + sAICombatScript == "ai_a_ranged" || + sAICombatScript == "ai_a_taunter" || + sAICombatScript == "ai_a_cntrspell" || + sAICombatScript == "ai_a_peaceful"); + } + return (sAIScript != sAICombatScript); +} diff --git a/_module/nss/0c_remove_effect.nss b/_module/nss/0c_remove_effect.nss new file mode 100644 index 00000000..aa95a0ce --- /dev/null +++ b/_module/nss/0c_remove_effect.nss @@ -0,0 +1,14 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script:0c_remove_effect + Programmer:Philos +//////////////////////////////////////////////////////////////////////////////// + Actions Taken script that removes an effect from OBJECT_SELF. + Param: nEffect - the EFFECT_TYPE_* number to remove. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_spells" +void main () +{ + int nEffect = StringToInt (GetScriptParam ("nEffectType")); + ai_RemoveASpecificEffect (OBJECT_SELF, nEffect); +} + diff --git a/_module/nss/0c_summon_assoc.nss b/_module/nss/0c_summon_assoc.nss new file mode 100644 index 00000000..19544de2 --- /dev/null +++ b/_module/nss/0c_summon_assoc.nss @@ -0,0 +1,17 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// + Script Name: 0c_summon_assoc + Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Conversation script to have the caller summon either an animal companion or + familiar associate. + + Param + sAssociate - which associate to summon. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + string sAssociate = GetScriptParam ("sAssociate"); + if (sAssociate == "Familiar") SummonFamiliar (); + else if (sAssociate == "Companion") SummonAnimalCompanion (); +} diff --git a/_module/nss/0c_use_feat.nss b/_module/nss/0c_use_feat.nss new file mode 100644 index 00000000..ec3ddcca --- /dev/null +++ b/_module/nss/0c_use_feat.nss @@ -0,0 +1,15 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// + Script Name: 0c_summon_assoc + Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Conversation script to have the caller use nFeat from the feat.2da. + + Param + nFeat - Feat number from the feat.2da. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + int nFeat = StringToInt (GetScriptParam ("nFeat")); + ActionUseFeat(nFeat, OBJECT_SELF); +} diff --git a/_module/nss/0e_c2_1_hb.nss b/_module/nss/0e_c2_1_hb.nss new file mode 100644 index 00000000..05fbfc2c --- /dev/null +++ b/_module/nss/0e_c2_1_hb.nss @@ -0,0 +1,16 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_c2_1_hb + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnHeartbeat script; + This will usually fire every 6 seconds (1 game round). + + I am reverting the AI script back to the games default scripts for efficiency. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_module" +void main() +{ + SetLocalInt(OBJECT_SELF, AI_ONSPAWN_EVENT, TRUE); + ai_ChangeEventScriptsForMonster(OBJECT_SELF); + ExecuteScript("nw_c2_default1"); +} diff --git a/_module/nss/0e_c2_7_ondeath.nss b/_module/nss/0e_c2_7_ondeath.nss new file mode 100644 index 00000000..7d9570eb --- /dev/null +++ b/_module/nss/0e_c2_7_ondeath.nss @@ -0,0 +1,34 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_c2_7_ondeath + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnDeath script; + This fires when the creature dies. +*//////////////////////////////////////////////////////////////////////////////// +#include "0i_module" +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 = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oCreature); + if(oAssociate != OBJECT_INVALID) + { + SetIsDestroyable(FALSE, FALSE, FALSE, oAssociate); + DelayCommand(0.1, ChangeToStandardFaction(oAssociate, STANDARD_FACTION_HOSTILE)); + DelayCommand(3.0, SetIsDestroyable(TRUE, FALSE, FALSE, oAssociate)); + } + } + } + if(GetLocalInt(oModule, AI_RULE_CORPSES_STAY)) SetIsDestroyable(FALSE, FALSE, TRUE); + ai_ClearCombatState(oCreature); + ExecuteScript(GetLocalString(oCreature, "AI_ON_DEATH")); +} + diff --git a/_module/nss/0e_ch_1_hb.nss b/_module/nss/0e_ch_1_hb.nss new file mode 100644 index 00000000..d04eeb8a --- /dev/null +++ b/_module/nss/0e_ch_1_hb.nss @@ -0,0 +1,14 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_ch_1_hb + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associate(Summons, Familiar, Companion) OnHeart beat script when out of combat; + This will usually fire every 6 seconds (1 game round). +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_module" +void main() +{ + SetLocalInt(OBJECT_SELF, AI_ONSPAWN_EVENT, TRUE); + ai_ChangeEventScriptsForAssociate(OBJECT_SELF); + ExecuteScript("nw_ch_ac1"); +} diff --git a/_module/nss/0e_ch_7_ondeath.nss b/_module/nss/0e_ch_7_ondeath.nss new file mode 100644 index 00000000..bb365524 --- /dev/null +++ b/_module/nss/0e_ch_7_ondeath.nss @@ -0,0 +1,42 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_ch_7_ondeath + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associate OnSpawn script; + This fires when an associate dies. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_module" +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))); + object oModule = GetModule(); + if(GetLocalInt(oModule, AI_RULE_PERM_ASSOC)) + { + object oAssociate; + int nIndex; + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oCreature); + if(oAssociate != OBJECT_INVALID) + { + 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) + { + 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")); +} + diff --git a/_module/nss/0e_do_combat_rnd.nss b/_module/nss/0e_do_combat_rnd.nss new file mode 100644 index 00000000..f1cb9c00 --- /dev/null +++ b/_module/nss/0e_do_combat_rnd.nss @@ -0,0 +1,22 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_do_combat_rnd + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Used to execute a combat round just after the current action is over. + Note: Do not use with an attack action since it will continue until + the attacked enemy is dead. We end attack actions with a ClearAllActions + command and would also end this one so it will not work with attack actions. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + if(AI_DEBUG) ai_Debug("0e_do_combat_rnd", "14", GetName(oCreature) + " is calculating a new round." + + "nAction: " + IntToString(GetCurrentAction(oCreature))); + if(ai_GetIsInCombat(oCreature)) + { + if(GetAssociateType(oCreature) == ASSOCIATE_TYPE_NONE && + !ai_GetIsCharacter(oCreature)) ai_DoMonsterCombatRound(oCreature); + else if(ai_CanIAttack(oCreature)) ai_DoAssociateCombatRound(oCreature); + } +} diff --git a/_module/nss/0e_gui_events.nss b/_module/nss/0e_gui_events.nss new file mode 100644 index 00000000..401c8254 --- /dev/null +++ b/_module/nss/0e_gui_events.nss @@ -0,0 +1,60 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: 0e_gui_events + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + OnPlayerGUIEvent event script + Used to allow PEPS to gain control of specific GUI events. + +/*////////////////////////////////////////////////////////////////////////////// +#include "0i_gui_events" +#include "0i_menus" +void main() +{ + object oPC = GetLastGuiEventPlayer(); + int nEventType = GetLastGuiEventType(); + int nEventInt = GetLastGuiEventInteger(); + //object oEventObject = GetLastGuiEventObject(); + switch(nEventType) + { + case GUIEVENT_EFFECTICON_CLICK: + { + if(ai_GetMagicMode(oPC, AI_MAGIC_EFFECT_ICON_REPORT)) + { + ai_CreateEffectChatReport(oPC, nEventInt); + return; + } + int nToken = NuiFindWindow(oPC, AI_EFFECT_ICON_NUI); + json jData; + if(nToken) + { + jData = NuiGetUserData(oPC, nToken); + int nOldEffectIcon = JsonGetInt(JsonArrayGet(jData, 1)); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + if(nOldEffectIcon == nEventInt) return; + } + ai_CreateEffectIconMenu(oPC, nEventInt); + } + case GUIEVENT_PARTYBAR_PORTRAIT_CLICK: + { + object oAssociate = GetLastGuiEventObject(); + if(GetMaster(oAssociate) == oPC) + { + // If all the Command buttons are blocked then don't load the menu. + if(GetLocalInt(GetModule(), sDMWidgetAccessVarname) != 7340028) + { + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + if(IsWindowClosed(oPC, sAssociateType + AI_COMMAND_NUI)) + { + ai_CreateAssociateCommandNUI(oPC, oAssociate); + } + IsWindowClosed(oPC, sAssociateType + AI_NUI); + IsWindowClosed(oPC, sAssociateType + AI_LOOTFILTER_NUI); + IsWindowClosed(oPC, sAssociateType + AI_COPY_NUI); + IsWindowClosed(oPC, sAssociateType + AI_QUICK_WIDGET_NUI); + IsWindowClosed(oPC, sAssociateType + AI_SPELL_MEMORIZE_NUI); + IsWindowClosed(oPC, sAssociateType + AI_SPELL_KNOWN_NUI); + } + } + } + } +} diff --git a/_module/nss/0e_id_events.nss b/_module/nss/0e_id_events.nss new file mode 100644 index 00000000..c68e74c9 --- /dev/null +++ b/_module/nss/0e_id_events.nss @@ -0,0 +1,277 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0e_id_events +//////////////////////////////////////////////////////////////////////////////// + Infinite Dungeons monster event handler. +*/////////////////////////////////////////////////////////////////////////////// +#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"); + //WriteTimestampedLogEntry("0e_id_events [24] " + GetName(oCreature) + " nEvent: " + IntToString(nEvent) + + // " bFollower: " + IntToString(bFollower)); + switch (nEvent) + { + case EVENT_SCRIPT_CREATURE_ON_HEARTBEAT: + { + if(bFollower) ai_hen_id1_heart(oCreature); + else ExecuteScript("nw_c2_default1", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_NOTICE: + { + if(bFollower) ai_hen_id1_percept(oCreature); + else ExecuteScript("nw_c2_default2", 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); + break; + } + case EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED: + { + if(bFollower) ExecuteScript("nw_ch_ac5", oCreature); + else ExecuteScript("nw_c2_default5", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_DAMAGED: + { + if(bFollower) ExecuteScript("nw_ch_ac6", oCreature); + else ExecuteScript("nw_c2_default6", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT: + { + if(bFollower) ai_hen_id1_castat(oCreature); + else ExecuteScript("nw_c2_defaultb", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND: + { + ai_hen_id1_endcombat(oCreature, bFollower); + break; + } + case EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR: + { + if(bFollower) ExecuteScript("nw_ch_ace", oCreature); + else ExecuteScript("nw_c2_defaulte", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_RESTED: + { + if(bFollower) ExecuteScript("nw_ch_aca", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_DISTURBED: + { + if(bFollower) ExecuteScript("nw_ch_ac8", oCreature); + else ExecuteScript("nw_c2_default8", 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 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/_module/nss/0e_nui.nss b/_module/nss/0e_nui.nss new file mode 100644 index 00000000..aa5dd967 --- /dev/null +++ b/_module/nss/0e_nui.nss @@ -0,0 +1,1972 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: 0e_nui + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Menu event script + sEvent: close, click, mousedown, mouseup, watch (if bindwatch is set). +/*////////////////////////////////////////////////////////////////////////////// +#include "nw_inc_gff" +#include "x0_i0_assoc" +#include "0i_menus" +#include "0i_player_target" +// Save a window ID to the database. +void ai_SaveWindowLocation(object oPC, int nToken, string sAssociateType, string sWindowID); +// Sets the Widget Buttons state to sElem Checkbox state. +void ai_SetWidgetButtonToCheckbox(object oPC, int nButton, object oAssociate, string sAssociateType, int nToken, string sElem); +// Flips an AI Buttons state to sElem Checkbox state. +void ai_SetAIButtonToCheckbox(object oPC, int nButton, object oAssociate, string sAssociateType, int nToken, string sElem); +// Flips the flag for the loot filter to sElem Checkbox state. +void ai_SetLootFilterToCheckbox(object oPC, object oAssociate, int nFilterBit, int nToken, string sElem); +// Sets an associates companion type. Cannot set companion for a player! +void ai_SetCompanionType(object oPC, object oAssociate, int nToken, int nCompanionType); +// Sets an associates companion name. Cannot set companion for a player! +void ai_SetCompanionName(object oPC, object oAssociate, int nToken, int nCompanionType); +// Sets an associates AI script via a combo box. +void ai_SetAIScript(object oPC, object oAssociate, int nToken); +// Increments/Decrements the Perception Range use variable for the AI. +void ai_PercRangeIncrement(object oPC, object oAssociate, int nIncrement, string sAssociateType, int nToken); +// Saves an associates perception range changed on the button. +void ai_Perc_Range(object oPC, object oAssociate, int nToken, string sAssociateType); +// Changes Perception Distance Rule for monsters. +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); + +void ai_SaveWindowLocation(object oPC, int nToken, string sAssociateType, string sWindowID) +{ + json jGeometry = NuiGetBind(oPC, nToken, "window_geometry"); + float fX = JsonGetFloat(JsonObjectGet(jGeometry, "x")); + float fY = JsonGetFloat(JsonObjectGet(jGeometry, "y")); + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) jLocations = JsonObject(); + json jWindow = JsonObjectGet(jLocations, sWindowID); + if(JsonGetType(jWindow) == JSON_TYPE_NULL) jWindow = JsonObject(); + jWindow = JsonObjectSet(jWindow, "x", JsonFloat(fX)); + jWindow = JsonObjectSet(jWindow, "y", JsonFloat(fY)); + jLocations = JsonObjectSet(jLocations, sWindowID, jWindow); + //SendMessageToPC(oPC, "0e_nui, 52, sAssociateType: " + sAssociateType + + // " sWindowID: " + sWindowID + + // " jLocations: " + JsonDump(jLocations, 1)); + ai_SetAssociateDbJson(oPC, sAssociateType, "locations", jLocations); +} +void ai_ToggleAssociateWidgetOnOff(object oPC, int nToken, object oAssociate, string sAssociateType) +{ + string sText, sText2, sName = GetName(oAssociate); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int bWidget = !ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType); + ai_SetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType, bWidget); + NuiSetBind(oPC, nToken, "btn_widget_onoff", JsonBool (!bWidget)); + if(bWidget) + { + sText = "on"; + sText2 = "Off"; + IsWindowClosed(oPC, sAssociateType + AI_WIDGET_NUI); + } + else + { + sText = "off"; + sText2 = "On"; + ai_CreateWidgetNUI(oPC, oAssociate); + } + NuiSetBind(oPC, nToken, "btn_widget_onoff_label", JsonString("Widget " + sText2)); + NuiSetBind(oPC, nToken, "btn_widget_onoff_tooltip", JsonString(" Turn " + sName + " widget " + sText)); +} +void main() +{ + object oPC = NuiGetEventPlayer(); + int nToken = NuiGetEventWindow(); + string sEvent = NuiGetEventType(); + string sElem = NuiGetEventElement(); + int nIndex = NuiGetEventArrayIndex(); + string sWndId = NuiGetWindowId(oPC, nToken); + //SendMessageToPC(oPC, "0e_nui , 64 sWndId: " + sWndId + " sEvent: " + sEvent + " sElem: " + sElem + + // " nToken: " + IntToString(nToken) + " nIndex: " + IntToString(nIndex) + + // " oPC: " + GetName(oPC)); + // Get if the menu has an associate attached. + json jData = NuiGetUserData(oPC, nToken); + object oAssociate = StringToObject(JsonGetString(JsonArrayGet(jData, 0))); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + 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); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + return; + } + if(sAssociateType == "") return; + //************************************************************************** + // Watch to see if the window moves and save. + if(sElem == "window_geometry" && sEvent == "watch") + { + if(GetLocalInt(oPC, AI_NO_NUI_SAVE)) return; + // If the widget is locked then don't save. + if(sWndId == sAssociateType + AI_WIDGET_NUI && + ai_GetWidgetButton(oPC, BTN_WIDGET_LOCK, oAssociate, sAssociateType)) return; + ai_SaveWindowLocation(oPC, nToken, sAssociateType, sWndId); + return; + } + //************************************************************************** + // Main AI events. + if(sWndId == AI_MAIN_NUI) + { + //if(GetLocalInt(oPC, AI_NO_NUI_SAVE)) return; + if(sEvent == "click") + { + if(sElem == "btn_plugin_manager") + { + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + ai_CreatePluginNUI(oPC); + } + else if(sElem == "btn_action_ghost") + { + // We set ghost mode differently for each AI. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + if(GetLocalInt(oPC, sGhostModeVarname)) + { + DeleteLocalInt(oPC, sGhostModeVarname); + ai_SendMessages("Action Ghost mode is turned off when using commands.", AI_COLOR_YELLOW, oPC); + object oAssociate; + int nIndex; + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) + { + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); + } + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) + { + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); + } + } + } + else + { + SetLocalInt(oPC, sGhostModeVarname, TRUE); + ai_SendMessages("Action Ghost mode is turned on when using commands.", AI_COLOR_YELLOW, oPC); + } + } + else + { + if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST)) + { + ai_SetAIMode(oPC, AI_MODE_ACTION_GHOST, FALSE); + ai_SendMessages("Action Ghost mode is turned off when using commands.", AI_COLOR_YELLOW, oPC); + 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)) + { + 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)) + { + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); + } + } + } + else + { + ai_SetAIMode(oPC, AI_MODE_ACTION_GHOST); + ai_SendMessages("Action Ghost mode is turned on when using commands.", AI_COLOR_YELLOW, oPC); + } + 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)) + { + ai_SetMagicMode(oPC, AI_MAGIC_EFFECT_ICON_REPORT, FALSE); + ai_SendMessages("All effect icons will be reported in a menu at the top of the screen.", AI_COLOR_YELLOW, oPC); + } + else + { + ai_SetMagicMode(oPC, AI_MAGIC_EFFECT_ICON_REPORT); + ai_SendMessages("All effect icons will be reported in the chat screen.", AI_COLOR_YELLOW, oPC); + } + aiSaveAssociateModesToDb(oPC, oPC); + } + if(sElem == "btn_default_xp") + { + int nDefaultXP = GetLocalInt(GetModule(), AI_RULE_DEFAULT_XP_SCALE); + SetModuleXPScale(nDefaultXP); + NuiSetBind(oPC, nToken, "txt_xp_scale", JsonString(IntToString(nDefaultXP))); + } + } + if(sEvent == "watch") + { + string sPreElem = GetStringLeft(sElem, 4); + if(sPreElem == "txt_") + { + object oModule = GetModule(); + json jRules = ai_GetCampaignDbJson("rules"); + string sText = JsonGetString(NuiGetBind(oPC, nToken, sElem)); + if(sElem == "txt_max_henchman") + { + int nMaxHenchmen = StringToInt(sText); + if(nMaxHenchmen < 1) nMaxHenchmen = 1; + if(nMaxHenchmen > 12) + { + nMaxHenchmen = 12; + ai_SendMessages("The maximum henchmen for this mod is 12!", AI_COLOR_RED, oPC); + } + SetMaxHenchmen(nMaxHenchmen); + SetLocalInt(oModule, AI_RULE_MAX_HENCHMAN, nMaxHenchmen); + jRules = JsonObjectSet(jRules, AI_RULE_MAX_HENCHMAN, JsonInt(nMaxHenchmen)); + ai_SendMessages("Maximum henchmen has been changed to " + IntToString(nMaxHenchmen), AI_COLOR_YELLOW, oPC); + } + else if(sElem == "txt_ai_difficulty") + { + int nChance = StringToInt(sText); + if(nChance < 0) nChance = 0; + else if(nChance > 100) nChance = 100; + SetLocalInt(oModule, AI_RULE_AI_DIFFICULTY, nChance); + jRules = JsonObjectSet(jRules, AI_RULE_AI_DIFFICULTY, JsonInt(nChance)); + } + else if(sElem == "txt_perception_distance") + { + float fDistance = StringToFloat(sText); + if(fDistance < 10.0) fDistance = 10.0; + else if(fDistance > 60.0) fDistance = 60.0; + SetLocalFloat(oModule, AI_RULE_PERCEPTION_DISTANCE, fDistance); + jRules = JsonObjectSet(jRules, AI_RULE_PERCEPTION_DISTANCE, JsonFloat(fDistance)); + } + else if(sElem == "txt_inc_enc") + { + float fNumber = StringToFloat(sText); + if(fNumber < 0.0) fNumber = 0.0; + else if(fNumber > 9.0) fNumber = 9.0; + SetLocalFloat(oModule, AI_INCREASE_ENC_MONSTERS, fNumber); + jRules = JsonObjectSet(jRules, AI_INCREASE_ENC_MONSTERS, JsonFloat(fNumber)); + } + else if(sElem == "txt_inc_hp") + { + int nNumber = StringToInt(sText); + if(nNumber < 0) nNumber = 0; + else if(nNumber > 100) nNumber = 100; + SetLocalInt(oModule, AI_INCREASE_MONSTERS_HP, nNumber); + jRules = JsonObjectSet(jRules, AI_INCREASE_MONSTERS_HP, JsonInt(nNumber)); + } + else if(sElem == "txt_wander_distance") + { + float fDistance = StringToFloat(sText); + if(fDistance < 0.0) fDistance = 0.0; + else if(fDistance > 99.0) fDistance = 99.0; + SetLocalFloat(oModule, AI_RULE_WANDER_DISTANCE, fDistance); + jRules = JsonObjectSet(jRules, AI_RULE_WANDER_DISTANCE, JsonFloat(fDistance)); + } + else if(sElem == "txt_xp_scale") + { + int nNumber = StringToInt(sText); + if(nNumber < 0) nNumber = 0; + else if(nNumber > 200) nNumber = 200; + SetModuleXPScale(nNumber); + return; + } + ai_SetCampaignDbJson("rules", jRules); + } + else if(sPreElem == "chbx") + { + object oModule = GetModule(); + int bCheck = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + json jRules = ai_GetCampaignDbJson("rules"); + if(sElem == "chbx_moral_check") + { + SetLocalInt(oModule, AI_RULE_MORAL_CHECKS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_MORAL_CHECKS, JsonInt(bCheck)); + } + else if(sElem == "chbx_buff_monsters_check") + { + SetLocalInt(oModule, AI_RULE_BUFF_MONSTERS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_BUFF_MONSTERS, JsonInt(bCheck)); + } + else if(sElem == "chbx_buff_summons_check") + { + SetLocalInt(oModule, AI_RULE_PRESUMMON, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_PRESUMMON, JsonInt(bCheck)); + } + else if(sElem == "chbx_ambush_monsters_check") + { + SetLocalInt(oModule, AI_RULE_AMBUSH, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_AMBUSH, JsonInt(bCheck)); + } + else if(sElem == "chbx_companions_check") + { + SetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_SUMMON_COMPANIONS, JsonInt(bCheck)); + } + else if(sElem == "chbx_advanced_movement_check") + { + SetLocalInt(oModule, AI_RULE_ADVANCED_MOVEMENT, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_ADVANCED_MOVEMENT, JsonInt(bCheck)); + } + else if(sElem == "chbx_ilr_check") + { + SetLocalInt(oModule, AI_RULE_ILR, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_ILR, JsonInt(bCheck)); + } + else if(sElem == "chbx_umd_check") + { + SetLocalInt(oModule, AI_RULE_ALLOW_UMD, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_ALLOW_UMD, JsonInt(bCheck)); + } + else if(sElem == "chbx_use_healingkits_check") + { + SetLocalInt(oModule, AI_RULE_HEALERSKITS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_HEALERSKITS, JsonInt(bCheck)); + } + else if(sElem == "chbx_perm_assoc_check") + { + SetLocalInt(oModule, AI_RULE_PERM_ASSOC, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_PERM_ASSOC, JsonInt(bCheck)); + } + else if(sElem == "chbx_corpses_stay_check") + { + SetLocalInt(oModule, AI_RULE_CORPSES_STAY, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_CORPSES_STAY, JsonInt(bCheck)); + } + else if(sElem == "chbx_wander_check") + { + SetLocalInt(oModule, AI_RULE_WANDER, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_WANDER, JsonInt(bCheck)); + NuiSetBind(oPC, nToken, "txt_wander_distance_event", JsonBool(bCheck)); + } + else if(sElem == "chbx_open_doors_check") + { + SetLocalInt(oModule, AI_RULE_OPEN_DOORS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_OPEN_DOORS, JsonInt(bCheck)); + } + else if(sElem == "chbx_party_scale_check") + { + if(bCheck) + { + SetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP, GetModuleXPScale()); + ai_CheckXPPartyScale(oPC); + } + else + { + SetModuleXPScale(GetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE)); + } + SetLocalInt(oModule, AI_RULE_PARTY_SCALE, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_PARTY_SCALE, JsonInt(bCheck)); + string sText = IntToString(GetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP)); + NuiSetBind(oPC, nToken, "chbx_party_scale_tooltip", JsonString(" PEPS adjusts your XP based on party size from (" + sText + ").")); + sText = IntToString(GetModuleXPScale()); + NuiSetBind(oPC, nToken, "txt_xp_scale", JsonString(sText)); + } + else if(sElem == "chbx_darkness_check") + { + if(bCheck) + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_DARKNESS); + jRules = ai_AddRestrictedSpell(jRules, 159); + jRules = ai_AddRestrictedSpell(jRules, SPELLABILITY_AS_DARKNESS); + jRules = ai_AddRestrictedSpell(jRules, 688); // WildShape_Darkness + } + else + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_DARKNESS, FALSE); + jRules = ai_AddRestrictedSpell(jRules, 159, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELLABILITY_AS_DARKNESS, FALSE); + jRules = ai_AddRestrictedSpell(jRules, 688, FALSE); // WildShape_Darkness + } + } + else if(sElem == "chbx_dispels_check") + { + if(bCheck) + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_LESSER_DISPEL); + jRules = ai_AddRestrictedSpell(jRules, SPELL_DISPEL_MAGIC); + jRules = ai_AddRestrictedSpell(jRules, SPELL_GREATER_DISPELLING); + jRules = ai_AddRestrictedSpell(jRules, SPELL_MORDENKAINENS_DISJUNCTION); + } + else + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_LESSER_DISPEL, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELL_DISPEL_MAGIC, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELL_GREATER_DISPELLING, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELL_MORDENKAINENS_DISJUNCTION, FALSE); + } + } + else if(sElem == "chbx_timestop_check") + { + if(bCheck) jRules = ai_AddRestrictedSpell(jRules, SPELL_TIME_STOP); + else jRules = ai_AddRestrictedSpell(jRules, SPELL_TIME_STOP, FALSE); + } + ai_SetCampaignDbJson("rules", jRules); + } + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + // Follow range is only changed on non-pc's + if(sElem == "lbl_perc_dist") ai_RulePercDistInc(oPC, GetModule(), 1, nToken); + } + else if(nMouseScroll == -1.0) // Scroll down + { + // Follow range is only changed on non-pc's + if(sElem == "lbl_perc_dist") ai_RulePercDistInc(oPC, GetModule(), -1, nToken); + } + } + return; + } + //************************************************************************** + // Associate Command events. + if(sWndId == sAssociateType + AI_COMMAND_NUI) + { + if(sEvent == "click") + { + if(sElem == "btn_ai_menu") + { + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + ai_CreateAssociateAINUI(oPC, oAssociate); + } + if(sElem == "btn_vertical_widget") + { + int bVertical = !ai_GetWidgetButton(oPC, BTN_WIDGET_VERTICAL, oAssociate, sAssociateType); + ai_SetWidgetButton(oPC, BTN_WIDGET_VERTICAL, oAssociate, sAssociateType, bVertical); + if(oPC == oAssociate || + (oPC != oAssociate && !ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType))) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } + } + else if(sElem == "btn_main_menu") + { + if(ai_GetIsCharacter(oAssociate)) ai_CreateAIMainNUI(oPC); + } + else if(sElem == "btn_widget_onoff") + { + ai_ToggleAssociateWidgetOnOff(oPC, nToken, oAssociate, sAssociateType); + } + else if(sElem == "btn_widget_lock") + { + int bLocked = !ai_GetWidgetButton(oPC, BTN_WIDGET_LOCK, oAssociate, sAssociateType); + ai_SetWidgetButton(oPC, BTN_WIDGET_LOCK, oAssociate, sAssociateType, bLocked); + 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(sElem == "btn_copy_settings") + { + ai_CreateCopySettingsNUI(oPC, oAssociate); + } + else if(sElem == "btn_cmd_action") ai_Action(oPC, oAssociate); + else if(sElem == "btn_cmd_guard") ai_DoCommand(oPC, oAssociate, 1); + else if(sElem == "btn_cmd_hold") ai_DoCommand(oPC, oAssociate, 3); + else if(sElem == "btn_cmd_search") ai_DoCommand(oPC, oAssociate, 5); + else if(sElem == "btn_cmd_stealth") ai_DoCommand(oPC, oAssociate, 6); + else if(sElem == "btn_cmd_attack") ai_DoCommand(oPC, oAssociate, 4); + else if(sElem == "btn_cmd_follow") ai_DoCommand(oPC, oAssociate, 2); + else if(sElem == "btn_follow_target") ai_FollowTarget(oPC, oAssociate); + else if(sElem == "btn_cmd_ai_script") ai_AIScript(oPC, oAssociate, sAssociateType, nToken); + else if(sElem == "btn_cmd_place_trap") ai_HavePCPlaceTrap(oPC, oAssociate); + else if(sElem == "btn_quick_widget") + { + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + ai_CreateQuickWidgetSelectionNUI(oPC, oAssociate); + } + else if(sElem == "btn_spell_memorize") + { + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + ai_CreateSpellMemorizationNUI(oPC, oAssociate); + } + else if(sElem == "btn_spell_known") + { + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + ai_CreateSpellKnownNUI(oPC, oAssociate); + } + else if(sElem == "btn_buff_short") + { + ai_Buff_Button(oPC, oAssociate, 2, sAssociateType); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + } + else if(sElem == "btn_buff_long") + { + ai_Buff_Button(oPC, oAssociate, 3, sAssociateType); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + } + else if(sElem == "btn_buff_all") + { + ai_Buff_Button(oPC, oAssociate, 1, sAssociateType); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + } + else if(sElem == "btn_buff_rest") ai_Buff_Button(oPC, oAssociate, 0, sAssociateType); + else if(sElem == "btn_jump_to") ai_JumpToPC(oPC, oAssociate); + else if(sElem == "btn_ghost_mode") ai_GhostMode(oPC, oAssociate, nToken, sAssociateType); + else if(sElem == "btn_camera") ai_ChangeCameraView(oPC, oAssociate); + else if(sElem == "btn_inventory") ai_OpenInventory(oAssociate, oPC); + 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(sEvent == "watch") + { + if(sElem == "txt_familiar_name") + { + string sName = JsonGetString(NuiGetBind(oPC, nToken, sElem)); + if(sName != "") NuiSetBind(oPC, nToken, "btn_familiar_name_event", JsonBool(TRUE)); + else NuiSetBind(oPC, nToken, "btn_familiar_name_event", JsonBool(FALSE)); + } + if(GetStringLeft(sElem, 12) == "chbx_plugin_" && GetStringRight(sElem, 6) == "_check") + { + int nIndex = StringToInt(GetSubString(sElem, 12, 1)); + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + json jPlugin = JsonArrayGet(jPlugins, nIndex); + int bCheck = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + 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)); + } + else if(sElem == "chbx_buff_rest_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_BUFF_REST, 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); + else if(sElem == "chbx_cmd_search_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_SEARCH, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_cmd_stealth_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_STEALTH, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_cmd_attack_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_ATTACK, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_cmd_follow_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_FOLLOW, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_cmd_ai_script_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_AI_SCRIPT, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_cmd_place_trap_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_PLACE_TRAP, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_quick_widget_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_SPELL_WIDGET, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_follow_target_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_FOLLOW_TARGET, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_buff_short_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_BUFF_SHORT, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_buff_long_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_BUFF_LONG, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_buff_all_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_BUFF_ALL, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_jump_to_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_JUMP_TO, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_ghost_mode_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_GHOST_MODE, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_camera_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_CAMERA, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_inventory_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_INVENTORY, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_familiar_check") ai_SetWidgetButtonToCheckbox(oPC, BTN_CMD_FAMILIAR, oAssociate, sAssociateType, nToken, sElem); + 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)); + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + // Follow range is only changed on non-pc's + 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(nMouseScroll == -1.0) // Scroll down + { + // Follow range is only changed on non-pc's + 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); + } + } + return; + } + //************************************************************************** + // Associate AI events. + if(sWndId == sAssociateType + AI_NUI) + { + if(sEvent == "click") + { + if(sElem == "btn_command_menu") + { + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + ai_CreateAssociateCommandNUI(oPC, oAssociate); + } + if(sElem == "btn_main_menu") + { + if(ai_GetIsCharacter(oAssociate)) ai_CreateAIMainNUI(oPC); + } + else if(sElem == "btn_loot_filter") + { + ai_CreateLootFilterNUI(oPC, oAssociate); + } + else if(sElem == "btn_ai") + { + if(GetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT) == "xx_pc_1_hb") ai_TurnOff(oPC, oAssociate, sAssociateType); + else ai_TurnOn(oPC, oAssociate, sAssociateType); + } + else if(sElem == "btn_quiet") ai_ReduceSpeech(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_ranged") AssignCommand(oAssociate, ai_Ranged(oPC, oAssociate, sAssociateType)); + else if(sElem == "btn_equip_weapon") ai_EquipWeapons(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_search") ai_Search(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_stealth") ai_Stealth(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_open_door") ai_OpenDoor(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_traps") ai_Traps(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_pick_locks") ai_Locks(oPC, oAssociate, sAssociateType, 1); + else if(sElem == "btn_bash_locks") ai_Locks(oPC, oAssociate, sAssociateType, 2); + else if(sElem == "btn_magic") ai_UseMagic(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_magic_items") ai_UseMagicItems(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_def_magic") ai_UseOffensiveMagic(oPC, oAssociate, TRUE, FALSE, sAssociateType); + else if(sElem == "btn_off_magic") ai_UseOffensiveMagic(oPC, oAssociate, FALSE, TRUE, sAssociateType); + else if(sElem == "btn_spontaneous") ai_Spontaneous(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_heals_onoff") ai_Heal_OnOff(oPC, oAssociate, sAssociateType, 1); + else if(sElem == "btn_healp_onoff") ai_Heal_OnOff(oPC, oAssociate, sAssociateType, 2); + else if(sElem == "btn_cure_onoff") ai_Cure_OnOff(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_loot") ai_Loot(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_ignore_assoc") ai_Ignore_Associates(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_ignore_traps") ai_Ignore_Traps(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_perc_range") ai_Perc_Range(oPC, oAssociate, nToken, sAssociateType); + else if(sElem == "btn_ai_script") ai_SaveAIScript(oPC, oAssociate, nToken); + } + else if(sEvent == "watch") + { + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + if(sElem == "chbx_ai_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_FOR_PC, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_quiet_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_REDUCE_SPEECH, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_ranged_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_USE_RANGED, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_equip_weapon_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_STOP_WEAPON_EQUIP, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_search_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_USE_SEARCH, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_stealth_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_USE_STEALTH, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_open_door_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_OPEN_DOORS, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_traps_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_REMOVE_TRAPS, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_pick_locks_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_PICK_LOCKS, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_bash_locks_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_BASH_LOCKS, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_magic_level_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_MAGIC_LEVEL, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_spontaneous_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_NO_SPONTANEOUS, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_magic_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_NO_MAGIC_USE, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_magic_items_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_NO_MAGIC_ITEM_USE, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_def_magic_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_DEF_MAGIC_USE, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_off_magic_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_OFF_MAGIC_USE, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_heal_out_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_HEAL_OUT, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_heal_in_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_HEAL_IN, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_heals_onoff_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_STOP_SELF_HEALING, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_healp_onoff_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_STOP_PARTY_HEALING, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_cure_onoff_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_STOP_CURE_SPELLS, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_loot_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_LOOT, oAssociate, sAssociateType, nToken, sElem); + else if(sElem == "chbx_ignore_assoc_check") ai_SetAIButtonToCheckbox(oPC, BTN_AI_IGNORE_ASSOCIATES, oAssociate, sAssociateType, nToken, sElem); + 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)); + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + if(sElem == "btn_magic_level") ai_MagicIncrement(oPC, oAssociate, 1, sAssociateType); + else if(sElem == "btn_open_door") ai_OpenDoorIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_traps") ai_TrapRangeIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_pick_locks") ai_LockRangeIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_bash_locks") ai_LockRangeIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_heal_out") ai_Heal_Button(oPC, oAssociate, 5, AI_HEAL_OUT_OF_COMBAT_LIMIT, sAssociateType); + 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, nToken); + } + else if(nMouseScroll == -1.0) // Scroll down + { + if(sElem == "btn_magic_level") ai_MagicIncrement(oPC, oAssociate, -1, sAssociateType); + else if(sElem == "btn_open_door") ai_OpenDoorIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_traps") ai_TrapRangeIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_pick_locks") ai_LockRangeIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_bash_locks") ai_LockRangeIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_heal_out") ai_Heal_Button(oPC, oAssociate, -5, AI_HEAL_OUT_OF_COMBAT_LIMIT, sAssociateType); + 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, nToken); + } + } + return; + } + //************************************************************************** + // Associate Widget events. + if(sWndId == sAssociateType + AI_WIDGET_NUI) + { + if(sEvent == "click") + { + if(sElem == "btn_open_main") + { + // If all the Command buttons are blocked then don't load the menu. + if(GetLocalInt(GetModule(), sDMWidgetAccessVarname) != 7340028) + { + if(IsWindowClosed(oPC, sAssociateType + AI_COMMAND_NUI)) + { + ai_CreateAssociateCommandNUI(oPC, oAssociate); + } + IsWindowClosed(oPC, sAssociateType + AI_NUI); + IsWindowClosed(oPC, sAssociateType + AI_LOOTFILTER_NUI); + IsWindowClosed(oPC, sAssociateType + AI_COPY_NUI); + IsWindowClosed(oPC, sAssociateType + AI_QUICK_WIDGET_NUI); + IsWindowClosed(oPC, sAssociateType + AI_SPELL_MEMORIZE_NUI); + IsWindowClosed(oPC, sAssociateType + AI_SPELL_KNOWN_NUI); + if(ai_GetIsCharacter(oAssociate)) + { + IsWindowClosed(oPC, AI_MAIN_NUI); + IsWindowClosed(oPC, AI_PLUGIN_NUI); + } + } + } + else if(sElem == "btn_ai") + { + if(GetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT) == "xx_pc_1_hb") + { + ai_TurnOff(oPC, oAssociate, sAssociateType); + } + else ai_TurnOn(oPC, oAssociate, sAssociateType); + } + else if(sElem == "btn_quiet") ai_ReduceSpeech(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_ranged") AssignCommand(oAssociate, ai_Ranged(oPC, oAssociate, sAssociateType)); + else if(sElem == "btn_equip_weapon") ai_EquipWeapons(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_search") ai_Search(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_stealth") ai_Stealth(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_open_door") ai_OpenDoor(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_traps") ai_Traps(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_pick_locks") ai_Locks(oPC, oAssociate, sAssociateType, 1); + else if(sElem == "btn_bash_locks") ai_Locks(oPC, oAssociate, sAssociateType, 2); + else if(sElem == "btn_magic_minus") ai_MagicIncrement(oPC, oAssociate, -1, sAssociateType); + else if(sElem == "btn_magic_plus") ai_MagicIncrement(oPC, oAssociate, 1, sAssociateType); + else if(sElem == "btn_magic") ai_UseMagic(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_magic_items") ai_UseMagicItems(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_def_magic") ai_UseOffensiveMagic(oPC, oAssociate, TRUE, FALSE, sAssociateType); + else if(sElem == "btn_off_magic") ai_UseOffensiveMagic(oPC, oAssociate, FALSE, TRUE, sAssociateType); + else if(sElem == "btn_cure_onoff") ai_Cure_OnOff(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_loot") ai_Loot(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_ignore_assoc") ai_Ignore_Associates(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_ignore_traps") ai_Ignore_Traps(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_perc_range") ai_Perc_Range(oPC, oAssociate, nToken, sAssociateType); + else if(sElem == "btn_spontaneous") ai_Spontaneous(oPC, oAssociate, sAssociateType); + else if(sElem == "btn_buff_short") + { + ai_Buff_Button(oPC, oAssociate, 2, sAssociateType); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + } + else if(sElem == "btn_buff_long") + { + ai_Buff_Button(oPC, oAssociate, 3, sAssociateType); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + } + else if(sElem == "btn_buff_all") + { + ai_Buff_Button(oPC, oAssociate, 1, sAssociateType); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + } + else if(sElem == "btn_buff_rest") ai_Buff_Button(oPC, oAssociate, 0, sAssociateType); + else if(sElem == "btn_jump_to") ai_JumpToPC(oPC, oAssociate); + else if(sElem == "btn_ghost_mode") ai_GhostMode(oPC, oAssociate, nToken, sAssociateType); + else if(sElem == "btn_camera") ai_ChangeCameraView(oPC, oAssociate); + else if(sElem == "btn_inventory") ai_OpenInventory(oAssociate, oPC); + else if(sElem == "btn_familiar") + { + if(GetHasFeat(FEAT_SUMMON_FAMILIAR, oAssociate)) + { + DecrementRemainingFeatUses(oAssociate, FEAT_SUMMON_FAMILIAR); + SummonFamiliar(oAssociate); + } + } + else if(sElem == "btn_companion") + { + if(GetHasFeat(FEAT_ANIMAL_COMPANION, oAssociate)) + { + DecrementRemainingFeatUses(oAssociate, FEAT_ANIMAL_COMPANION); + SummonAnimalCompanion(oAssociate); + } + } + else if(sElem == "btn_heals_onoff") ai_Heal_OnOff(oPC, oAssociate, sAssociateType, 1); + else if(sElem == "btn_healp_onoff") ai_Heal_OnOff(oPC, oAssociate, sAssociateType, 2); + else if(sElem == "btn_cmd_action") ai_Action(oPC, oAssociate); + else if(sElem == "btn_cmd_guard") ai_DoCommand(oPC, oAssociate, 1); + else if(sElem == "btn_cmd_hold") ai_DoCommand(oPC, oAssociate, 3); + else if(sElem == "btn_cmd_search") ai_DoCommand(oPC, oAssociate, 5); + else if(sElem == "btn_cmd_stealth") ai_DoCommand(oPC, oAssociate, 6); + else if(sElem == "btn_cmd_attack") ai_DoCommand(oPC, oAssociate, 4); + else if(sElem == "btn_cmd_follow") ai_DoCommand(oPC, oAssociate, 2); + else if(sElem == "btn_cmd_ai_script") ai_AIScript(oPC, oAssociate, sAssociateType, nToken); + else if(sElem == "btn_cmd_place_trap") ai_HavePCPlaceTrap(oPC, oAssociate); + else if(sElem == "btn_follow_target") ai_FollowTarget(oPC, oAssociate); + 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); + } + if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + 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_magic_level") ai_MagicIncrement(oPC, oAssociate, 1, sAssociateType); + else if(sElem == "btn_pick_locks") ai_LockRangeIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_bash_locks") ai_LockRangeIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_traps") ai_TrapRangeIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_open_door") ai_OpenDoorIncrement(oPC, oAssociate, 1.0, sAssociateType); + else if(sElem == "btn_heal_out") ai_Heal_Button(oPC, oAssociate, 5, AI_HEAL_OUT_OF_COMBAT_LIMIT, sAssociateType); + 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); + } + if(nMouseScroll == -1.0) // Scroll down + { + 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_magic_plus") ai_MagicIncrement(oPC, oAssociate, -1, sAssociateType); + if(sElem == "btn_magic_level") ai_MagicIncrement(oPC, oAssociate, -1, sAssociateType); + else if(sElem == "btn_pick_locks") ai_LockRangeIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_bash_locks") ai_LockRangeIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_traps") ai_TrapRangeIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_open_door") ai_OpenDoorIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(sElem == "btn_heal_out") ai_Heal_Button(oPC, oAssociate, -5, AI_HEAL_OUT_OF_COMBAT_LIMIT, sAssociateType); + 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); + } + } + if(sEvent == "mousedown") + { + int nMouseButton = JsonGetInt(JsonObjectGet(NuiGetEventPayload(), "mouse_btn")); + if(nMouseButton == NUI_MOUSE_BUTTON_RIGHT) + { + AssignCommand(oPC, PlaySound("gui_button")); + if(sElem == "btn_open_main") + { + // If all the AI buttons are blocked then don't load the menu. + if(GetLocalInt(GetModule(), sDMAIAccessVarname) != 203423743) + { + if(IsWindowClosed(oPC, sAssociateType + AI_NUI)) + { + ai_CreateAssociateAINUI(oPC, oAssociate); + } + } + IsWindowClosed(oPC, sAssociateType + AI_COMMAND_NUI); + IsWindowClosed(oPC, sAssociateType + AI_LOOTFILTER_NUI); + IsWindowClosed(oPC, sAssociateType + AI_COPY_NUI); + IsWindowClosed(oPC, sAssociateType + AI_QUICK_WIDGET_NUI); + IsWindowClosed(oPC, sAssociateType + AI_SPELL_MEMORIZE_NUI); + IsWindowClosed(oPC, sAssociateType + AI_SPELL_KNOWN_NUI); + if(ai_GetIsCharacter(oAssociate)) + { + IsWindowClosed(oPC, AI_MAIN_NUI); + IsWindowClosed(oPC, AI_PLUGIN_NUI); + } + } + else if(sElem == "btn_follow_range") ai_FollowIncrement(oPC, oAssociate, -1.0, sAssociateType); + else if(GetStringLeft(sElem, 11) == "btn_widget_") + { + if(GetStringLength(sElem) == 13) nIndex = StringToInt(GetStringRight(sElem, 2)); + else nIndex = StringToInt(GetStringRight(sElem, 1)); + json jAIData = ai_GetAssociateDbJson(oPC, ai_GetAssociateType(oPC, oAssociate), "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + json jSpell = JsonArrayGet(jWidget, nIndex); + ai_CreateDescriptionNUI(oPC, jSpell); + } + } + } + return; + } + //************************************************************************** + // Associate Loot events. + if(sWndId == sAssociateType + AI_LOOTFILTER_NUI) + { + if(sEvent == "click") + { + if(sElem == "btn_set_all") + { + SetLocalInt(oPC, "AI_BLOCK_CHECKS", TRUE); + SetLocalInt(oAssociate, sLootFilterVarname, 65535); + int nIndex; + for(nIndex = 2; nIndex < 20; nIndex++) + { + NuiSetBind(oPC, nToken, "chbx_" + IntToString(nIndex) + "_check", JsonBool (TRUE)); + } + json jLootFilter = ai_GetAssociateDbJson(oPC, sAssociateType, "lootfilters"); + jLootFilter = JsonArraySet(jLootFilter, 1, JsonInt(65535)); + ai_SetAssociateDbJson(oPC, sAssociateType, "lootfilters", jLootFilter); + DelayCommand(1.0, DeleteLocalInt(oPC, "AI_BLOCK_CHECKS")); + } + else if(sElem == "btn_clear_all") + { + SetLocalInt(oPC, "AI_BLOCK_CHECKS", TRUE); + SetLocalInt(oAssociate, sLootFilterVarname, 0); + int nIndex; + for(nIndex = 2; nIndex < 20; nIndex++) + { + NuiSetBind(oPC, nToken, "chbx_" + IntToString(nIndex) + "_check", JsonBool (FALSE)); + } + json jLootFilter = ai_GetAssociateDbJson(oPC, sAssociateType, "lootfilters"); + jLootFilter = JsonArraySet(jLootFilter, 1, JsonInt(0)); + ai_SetAssociateDbJson(oPC, sAssociateType, "lootfilters", jLootFilter); + DelayCommand(1.0, DeleteLocalInt(oPC, "AI_BLOCK_CHECKS")); + } + } + else if(sEvent == "watch") + { + if(GetStringLeft(sElem, 5) == "chbx_") + { + if(GetLocalInt(oPC, "AI_BLOCK_CHECKS")) return; + if(sElem == "chbx_give_loot_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_GIVE_TO_PC, nToken, sElem); + else if(sElem == "chbx_2_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_PLOT, nToken, sElem); + else if(sElem == "chbx_3_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_ARMOR, nToken, sElem); + else if(sElem == "chbx_4_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_BELTS, nToken, sElem); + else if(sElem == "chbx_5_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_BOOTS, nToken, sElem); + else if(sElem == "chbx_6_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_CLOAKS, nToken, sElem); + else if(sElem == "chbx_7_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_GEMS, nToken, sElem); + else if(sElem == "chbx_8_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_GLOVES, nToken, sElem); + else if(sElem == "chbx_9_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_HEADGEAR, nToken, sElem); + else if(sElem == "chbx_10_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_JEWELRY, nToken, sElem); + else if(sElem == "chbx_11_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_MISC, nToken, sElem); + else if(sElem == "chbx_12_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_POTIONS, nToken, sElem); + else if(sElem == "chbx_13_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_SCROLLS, nToken, sElem); + else if(sElem == "chbx_14_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_SHIELDS, nToken, sElem); + else if(sElem == "chbx_15_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_WANDS_RODS_STAVES, nToken, sElem); + else if(sElem == "chbx_16_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_WEAPONS, nToken, sElem); + else if(sElem == "chbx_17_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_ARROWS, nToken, sElem); + else if(sElem == "chbx_18_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_BOLTS, nToken, sElem); + else if(sElem == "chbx_19_check") ai_SetLootFilterToCheckbox(oPC, oAssociate, AI_LOOT_BULLETS, nToken, sElem); + json jLootFilter = ai_GetAssociateDbJson(oPC, sAssociateType, "lootfilters"); + int nLootFilter = GetLocalInt(oAssociate, sLootFilterVarname); + jLootFilter = JsonArraySet(jLootFilter, 1, JsonInt(nLootFilter)); + ai_SetAssociateDbJson(oPC, sAssociateType, "lootfilters", jLootFilter); + } + else if(GetStringLeft(sElem, 4) == "txt_") + { + if(sElem == "txt_max_weight") + { + int nMaxWeight = StringToInt(JsonGetString(NuiGetBind(oPC, nToken, sElem))); + if(nMaxWeight > 1000) nMaxWeight = 1000; + if(nMaxWeight < 1) nMaxWeight = 1; + SetLocalInt(oAssociate, AI_MAX_LOOT_WEIGHT, nMaxWeight); + json jLootFilter = ai_GetAssociateDbJson(oPC, sAssociateType, "lootfilters"); + jLootFilter = JsonArraySet(jLootFilter, 0, JsonInt(nMaxWeight)); + ai_SetAssociateDbJson(oPC, sAssociateType, "lootfilters", jLootFilter); + return; + } + if(GetStringLeft(sElem, 9) == "txt_gold_") + { + int nAmount = StringToInt(JsonGetString(NuiGetBind(oPC, nToken, sElem))); + int nIndex; + if(GetStringLength(sElem) == 11) nIndex = StringToInt(GetStringRight(sElem, 2)); + else nIndex = StringToInt(GetStringRight(sElem, 1)); + SetLocalInt(oAssociate, AI_MIN_GOLD_ + IntToString(nIndex), nAmount); + json jLootFilter = ai_GetAssociateDbJson(oPC, sAssociateType, "lootfilters"); + jLootFilter = JsonArraySet(jLootFilter, nIndex, JsonInt(nAmount)); + ai_SetAssociateDbJson(oPC, sAssociateType, "lootfilters", jLootFilter); + } + } + } + return; + } + //************************************************************************** + // Associate Paste events. + if(sWndId == sAssociateType + AI_COPY_NUI) + { + if(sEvent == "click") + { + int nIndex, nAssociateType = GetAssociateType(oAssociate); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + object oAssoc; + string sAssocType; + json jModes = ai_GetAssociateDbJson(oPC, sAssociateType, "modes"); + json jButtons = ai_GetAssociateDbJson(oPC, sAssociateType, "buttons"); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jLootFilters = ai_GetAssociateDbJson(oPC, sAssociateType, "lootfilters"); + string sCombatScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + string sDefaultScript = GetLocalString(oAssociate, AI_DEFAULT_SCRIPT); + if(sElem == "btn_paste_all") + { + // Check all non-henchman associates. + for(nIndex = 2; nIndex < 6; nIndex++) + { + if(nAssociateType != nIndex) + { + oAssoc = GetAssociate(nIndex, oPC); + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetAssociateDbJson(oPC, sAssocType, "modes", jModes); + ai_SetAssociateDbJson(oPC, sAssocType, "buttons", jButtons); + ai_SetAssociateDbJson(oPC, sAssocType, "aidata", jAIData); + ai_SetAssociateDbJson(oPC, sAssocType, "lootfilters", jLootFilters); + SetLocalString(oAssoc, AI_COMBAT_SCRIPT, sCombatScript); + SetLocalString(oAssoc, AI_DEFAULT_SCRIPT, sDefaultScript); + if(oAssoc != OBJECT_INVALID) + { + // Clear the creatures Perception distance so we can + // repopulate the local variables. + SetLocalFloat(oAssoc, AI_ASSOC_PERCEPTION_DISTANCE, 0.0); + ai_CheckAssociateData(oPC, oAssoc, sAssocType); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType)) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssocType + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssoc)); + } + } + } + } + // Check all of our henchman. + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssoc = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetAssociateDbJson(oPC, sAssocType, "modes", jModes); + ai_SetAssociateDbJson(oPC, sAssocType, "buttons", jButtons); + ai_SetAssociateDbJson(oPC, sAssocType, "aidata", jAIData); + ai_SetAssociateDbJson(oPC, sAssocType, "lootfilters", jLootFilters); + SetLocalString(oAssoc, AI_COMBAT_SCRIPT, sCombatScript); + SetLocalString(oAssoc, AI_DEFAULT_SCRIPT, sDefaultScript); + // Clear the creatures Perception distance so we can + // repopulate the local variables. + SetLocalFloat(oAssoc, AI_ASSOC_PERCEPTION_DISTANCE, 0.0); + ai_CheckAssociateData(oPC, oAssoc, sAssocType); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType)) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssocType + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssoc)); + } + } + else break; + } + ai_SendMessages(GetName(oAssociate) + "'s settings have been copied to all associates.", AI_COLOR_GREEN, oPC); + return; + } + else if(GetStringLeft(sElem, 18) == "btn_paste_henchman") + { + int nIndex = StringToInt(GetStringRight(sElem, 1)); + oAssoc = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssoc != OBJECT_INVALID) + { + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetAssociateDbJson(oPC, sAssocType, "modes", jModes); + ai_SetAssociateDbJson(oPC, sAssocType, "buttons", jButtons); + ai_SetAssociateDbJson(oPC, sAssocType, "aidata", jAIData); + ai_SetAssociateDbJson(oPC, sAssocType, "lootfilters", jLootFilters); + SetLocalString(oAssoc, AI_COMBAT_SCRIPT, sCombatScript); + SetLocalString(oAssoc, AI_DEFAULT_SCRIPT, sDefaultScript); + // Clear the creatures Perception distance so we can + // repopulate the local variables. + SetLocalFloat(oAssoc, AI_ASSOC_PERCEPTION_DISTANCE, 0.0); + ai_CheckAssociateData(oPC, oAssoc, sAssocType); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType)) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssocType + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssoc)); + } + ai_SendMessages(GetName(oAssociate) + "'s settings have been copied to " + GetName(oAssoc) + ".", AI_COLOR_GREEN, oPC); + } + return; + } + else if(sElem == "btn_paste_familiar") nIndex = ASSOCIATE_TYPE_FAMILIAR; + else if(sElem == "btn_paste_companion") nIndex = ASSOCIATE_TYPE_ANIMALCOMPANION; + else if(sElem == "btn_paste_summons") nIndex = ASSOCIATE_TYPE_SUMMONED; + else if(sElem == "btn_paste_dominated") nIndex = ASSOCIATE_TYPE_DOMINATED; + if(nIndex > 1 && nIndex < 6) + { + oAssoc = GetAssociate(nIndex, oPC); + sAssocType = ai_GetAssociateType(oPC, oAssoc); + ai_SetAssociateDbJson(oPC, sAssocType, "modes", jModes); + ai_SetAssociateDbJson(oPC, sAssocType, "buttons", jButtons); + ai_SetAssociateDbJson(oPC, sAssocType, "aidata", jAIData); + ai_SetAssociateDbJson(oPC, sAssocType, "lootfilters", jLootFilters); + SetLocalString(oAssoc, AI_COMBAT_SCRIPT, sCombatScript); + SetLocalString(oAssoc, AI_DEFAULT_SCRIPT, sDefaultScript); + if(oAssoc != OBJECT_INVALID) + { + // Clear the creatures Perception distance so we can + // repopulate the local variables. + SetLocalFloat(oAssoc, AI_ASSOC_PERCEPTION_DISTANCE, 0.0); + ai_CheckAssociateData(oPC, oAssoc, sAssocType); + if(!ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssoc, sAssocType)) + { + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssocType + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssoc)); + } + ai_SendMessages(GetName(oAssociate) + "'s settings have been copied to " + GetName(oAssoc) + ".", AI_COLOR_GREEN, oPC); + } + } + } + return; + } + //************************************************************************** + // Plugins events. + if(sWndId == AI_PLUGIN_NUI) + { + if(sEvent == "click") + { + if(sElem == "btn_load_plugins") + { + 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_mod_set"); + jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_debug"); + jPlugins = ai_Plugin_Add(oPC, jPlugins, "pi_test"); + ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreatePluginNUI(oPC)); + } + if(sElem == "btn_load_m_mods") + { + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + jPlugins = ai_Plugin_Add(oPC, jPlugins, "mm_prc_spells"); + ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreatePluginNUI(oPC)); + } + if(sElem == "btn_check_plugins") + { + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + int nIndex; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + if(JsonGetInt(JsonArrayGet(jPlugin, 1)) < 3) + { + jPlugin = JsonArraySet(jPlugin, 1, JsonBool(TRUE)); + jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); + } + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + 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(sElem == "btn_clear_plugins") + { + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + int nIndex; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + if(JsonGetInt(JsonArrayGet(jPlugin, 1)) < 3) + { + jPlugin = JsonArraySet(jPlugin, 1, JsonBool(FALSE)); + jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); + } + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + 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)); + } + else if(sElem == "btn_add_plugin") + { + string sScript = JsonGetString(NuiGetBind (oPC, nToken, "txt_plugin")); + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + jPlugins = ai_Plugin_Add(oPC, jPlugins, sScript); + ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreatePluginNUI(oPC)); + } + else if(GetStringLeft(sElem, 18) == "btn_remove_plugin_") + { + int nIndex = StringToInt(GetStringRight(sElem, 1)); + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + jPlugins = JsonArrayDel(jPlugins, nIndex); + 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)); + } + else if(GetStringLeft(sElem, 11) == "btn_plugin_") ai_Plugin_Execute(oPC, sElem); + } + else if(sEvent == "watch") + { + if(GetStringLeft(sElem, 12) == "chbx_plugin_" && GetStringRight(sElem, 6) == "_check") + { + int nIndex = StringToInt(GetSubString(sElem, 12, 1)); + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + json jPlugin = JsonArrayGet(jPlugins, nIndex); + int bCheck = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + 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)); + } + } + return; + } + //************************************************************************** + // Quick Use Widget events. + if(sWndId == sAssociateType + AI_QUICK_WIDGET_NUI) + { + if(sEvent == "click") + { + if(GetStringLeft(sElem, 10) == "btn_class_") // Changes the class. + { + string sClassPosition = GetStringRight(sElem, 1); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + jSpells = JsonArraySet(jSpells, 0, JsonInt(StringToInt(sClassPosition))); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreateQuickWidgetSelectionNUI(oPC, oAssociate)); + } + else if(GetStringLeft(sElem, 10) == "btn_level_") // Changes the level. + { + string sLevel; + if(GetStringLength(sElem) == 12) sLevel = GetStringRight(sElem, 2); + else sLevel = GetStringRight(sElem, 1); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + jSpells = JsonArraySet(jSpells, 1, JsonInt(StringToInt(sLevel))); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreateQuickWidgetSelectionNUI(oPC, oAssociate)); + } + else if(sElem == "btn_text_spell") // Adds abilities to quick use widget. + { + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + if(JsonGetType(jWidget) == JSON_TYPE_NULL) + { + jWidget = JsonArray(); + if(JsonGetLength(jSpells) == 2) jSpells = JsonArrayInsert(jSpells, JsonArray()); + } + int nWidgetLength = JsonGetLength(jWidget); + if(nWidgetLength < 20) + { + json jData = NuiGetUserData(oPC, nToken); + json jQuickListArray = JsonArrayGet(jData, 1); + json jSpell = JsonArrayGet(jQuickListArray, nIndex); + jWidget = JsonArrayInsert(jWidget, jSpell); + jSpells = JsonArraySet(jSpells, 2, jWidget); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + ai_PopulateWidgetList(oPC, oAssociate, nToken, jWidget); + } + else ai_SendMessages("The quick widget can only have 20 abilities or spells!", AI_COLOR_RED, oPC); + } + else if(sElem == "btn_info_spell") + { + json jQuickListArray = JsonArrayGet(jData, 1); + json jSpell = JsonArrayGet(jQuickListArray, nIndex); + ai_CreateDescriptionNUI(oPC, jSpell); + } + else if(GetStringLeft(sElem, 11) == "btn_widget_") // Removes ability from quick use widget + { + string sIndex; + if(GetStringLength(sElem) == 13) sIndex = GetStringRight(sElem, 2); + else sIndex = GetStringRight(sElem, 1); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + jWidget = JsonArrayDel(jWidget, StringToInt(sIndex)); + jSpells = JsonArraySet(jSpells, 2, jWidget); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + ai_PopulateWidgetList(oPC, oAssociate, nToken, jWidget); + } + } + else if(sEvent == "close") + { + 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; + } + //************************************************************************** + // Spell Memorization events. + if(sWndId == sAssociateType + AI_SPELL_MEMORIZE_NUI) + { + if(sEvent == "click") + { + if(GetStringLeft(sElem, 10) == "btn_class_") // Changes the class. + { + string sClassPosition = GetStringRight(sElem, 1); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + jSpells = JsonArraySet(jSpells, 0, JsonInt(StringToInt(sClassPosition))); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreateSpellMemorizationNUI(oPC, oAssociate)); + } + else if(GetStringLeft(sElem, 10) == "btn_level_") // Changes the level. + { + string sLevel = GetStringRight(sElem, 1); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + jSpells = JsonArraySet(jSpells, 1, JsonInt(StringToInt(sLevel))); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreateSpellMemorizationNUI(oPC, oAssociate)); + } + else if(sElem == "btn_text_spell") // Adds spell to memorization. + { + 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)); + 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); + string sSlot; + while(nSlot < nMaxMemorizationSlot) + { + if(GetMemorizedSpellId(oAssociate, nClass, nLevel, nSlot) == -1) + { + SetMemorizedSpell(oAssociate, nClass, nLevel, nSlot, nSpell, FALSE); + 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++; + } + if(nSlot >= nMaxMemorizationSlot) ai_SendMessages("All spell memorization slots are full!", AI_COLOR_RED, oPC); + } + else if(sElem == "btn_info_spell") + { + json jSpellArray = JsonArrayGet(jData, 1); + int nSpell = JsonGetInt(JsonArrayGet(jSpellArray, nIndex)); + ai_CreateDescriptionNUI(oPC, JsonArray(), nSpell); + } + 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 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) + { + DelayCommand(0.0, NuiDestroy(oPC, nUIToken)); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } + } + return; + } + //************************************************************************** + // Spell Known events. + if(sWndId == sAssociateType + AI_SPELL_KNOWN_NUI) + { + if(sEvent == "click") + { + if(GetStringLeft(sElem, 10) == "btn_class_") // Changes the class. + { + string sClassPosition = GetStringRight(sElem, 1); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + jSpells = JsonArraySet(jSpells, 0, JsonInt(StringToInt(sClassPosition))); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreateSpellKnownNUI(oPC, oAssociate)); + } + else if(GetStringLeft(sElem, 10) == "btn_level_") // Changes the level. + { + string sLevel = GetStringRight(sElem, 1); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + jSpells = JsonArraySet(jSpells, 1, JsonInt(StringToInt(sLevel))); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreateSpellKnownNUI(oPC, oAssociate)); + } + else if(sElem == "btn_text_spell") // Adds spell to known list. + { + 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)); + json jSpellArray = JsonArrayGet(jData, 1); + int nSpell = JsonGetInt(JsonArrayGet(jSpellArray, nIndex)); + json jClassList = GetLocalJson(oAssociate, AI_CLASS_LIST_JSON); + // Get the correct class array. + int bAddList, nClassIndex = 0; + json jClass = JsonArrayGet(jClassList, nClassIndex); + while(JsonGetInt(GffGetInt(jClass, "Class")) != nClass) + { + jClass = JsonArrayGet(jClassList, ++nClassIndex); + } + string sLevel = IntToString(nLevel); + json jSpell, jKnownList = GffGetList(jClass, "KnownList" + sLevel); + if(JsonGetType(jKnownList) == JSON_TYPE_NULL) + { + bAddList = TRUE; + 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; + while(nSlot < nMaxKnownSlots) + { + jSpell = JsonArrayGet(jKnownList, nSlot); + if(JsonGetType(jSpell) == JSON_TYPE_NULL) + { + jSpell = GffAddWord(JsonObject(), "Spell", nSpell); + jSpell = JsonObjectSet(jSpell, "__struct_id", JsonInt(3)); + jKnownList = JsonArrayInsert(jKnownList, jSpell); + 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; + } + else if(JsonGetInt(GffGetWord(jSpell, "Spell")) == nSpell) + { + string sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + ai_SendMessages(sName + " is already in the known spell list!", AI_COLOR_RED, oPC); + return; + } + nSlot++; + } + if(nSlot >= nMaxKnownSlots) + { + ai_SendMessages("All known spell slots are full!", AI_COLOR_RED, oPC); + return; + } + if(bAddList) jClass = GffAddList(jClass, "KnownList" + sLevel, jKnownList); + else jClass = GffReplaceList(jClass, "KnownList" + sLevel, jKnownList); + jClassList = JsonArraySet(jClassList, nClassIndex, jClass); + SetLocalJson(oAssociate, AI_CLASS_LIST_JSON, jClassList); + } + else if(sElem == "btn_info_spell") + { + json jSpellArray = JsonArrayGet(jData, 1); + int nSpell = JsonGetInt(JsonArrayGet(jSpellArray, nIndex)); + ai_CreateDescriptionNUI(oPC, JsonArray(), nSpell); + } + else if(GetStringLeft(sElem, 10) == "btn_known_") // Remove a known 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); + // Check to see if there is a spell in this slot. + string sImageName = JsonGetString(NuiGetBind(oPC, nToken, "btn_known_" + sIndex + "_image")); + if(sImageName == "ctl_cg_btn_splvl") return; + json jClassList = GetLocalJson(oAssociate, AI_CLASS_LIST_JSON); + // Get the correct class array. + int nClassIndex = 0; + json jClass = JsonArrayGet(jClassList, nClassIndex); + while(JsonGetInt(GffGetInt(jClass, "Class")) != nClass) + { + jClass = JsonArrayGet(jClassList, ++nClassIndex); + } + string sLevel = IntToString(nLevel); + json jKnownList = GffGetList(jClass, "KnownList" + sLevel); + jKnownList = JsonArrayDel(jKnownList, StringToInt(sIndex)); + jClass = GffReplaceList(jClass, "KnownList" + sLevel, jKnownList); + jClassList = JsonArraySet(jClassList, nClassIndex, jClass); + SetLocalJson(oAssociate, AI_CLASS_LIST_JSON, jClassList); + SetLocalInt(oAssociate, "AI_KNOWN_SPELL_CHANGE", TRUE); + // Relist all known spells so they match the index. + int nMaxKnownSlots, nSpell; + string sName, sSpellIcon, sClass = IntToString(nClass); + string sSpellKnownTable = Get2DAString("classes", "SpellKnownTable", nClass); + json jSpell; + if(sSpellKnownTable != "") nMaxKnownSlots = StringToInt(Get2DAString(sSpellKnownTable, "SpellLevel" + IntToString(nLevel), GetLevelByClass(nClass, oAssociate) - 1)); + else nMaxKnownSlots = 20; + nIndex = 0; + while(nIndex < 20) + { + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_event", JsonBool(TRUE)); + if(nIndex < nMaxKnownSlots) + { + jSpell = JsonArrayGet(jKnownList, nIndex); + if(JsonGetType(jSpell) == JSON_TYPE_NULL) + { + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_tooltip", JsonString(" Empty known spell slot")); + } + else + { + nSpell = JsonGetInt(GffGetWord(jSpell, "Spell")); + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + //nMetaMagic = 255; + //nDomain = 0; + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_image", JsonString(sSpellIcon)); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevel) + ")")); + //sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, -1, -1, -1, nMetaMagic, nDomain); + //NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); + } + } + else + { + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); + //NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString("")); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_event", JsonBool(FALSE)); + } + ++nIndex; + } + } + } + else if(sEvent == "close") + { + if(GetLocalInt(oAssociate, "AI_KNOWN_SPELL_CHANGE")) + { + RemoveHenchman(oPC, oAssociate); + json jHenchman = ObjectToJson(oAssociate, TRUE); + json jClassList = GetLocalJson(oAssociate, AI_CLASS_LIST_JSON); + jHenchman = GffReplaceList(jHenchman, "ClassList", jClassList); + location lLocation = GetLocation(oAssociate); + int nFamiliar, nCompanion; + object oCompanion = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oAssociate); + if(oCompanion != OBJECT_INVALID) nFamiliar = TRUE; + oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oAssociate); + if(oCompanion != OBJECT_INVALID) nCompanion = TRUE; + AssignCommand(oAssociate, SetIsDestroyable(TRUE, FALSE, FALSE)); + DestroyObject(oAssociate); + oAssociate = ai_AddHenchman(oPC, jHenchman, lLocation, nFamiliar, nCompanion); + DeleteLocalInt(oAssociate, "AI_KNOWN_SPELL_CHANGE"); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + } + } + return; + } + //************************************************************************** + // Spell Description events. + if(sWndId == AI_SPELL_DESCRIPTION_NUI) + { + if(sEvent == "click" && sElem == "btn_ok") DelayCommand(0.0, NuiDestroy(oPC, nToken)); + } + //************************************************************************** + // Effect Icon NUI events. + if(sWndId == AI_EFFECT_ICON_NUI) + { + if(sEvent == "click") + { + if(GetStringLeft(sElem, 18) == "btn_remove_effect_") + { + int nEffectIndex = StringToInt(GetStringRight(sElem, GetStringLength(sElem) - 18)); + json jEffectID = JsonArrayGet(jData, 2); + string sEffectLinkID = JsonGetString(JsonArrayGet(jEffectID, nEffectIndex)); + int nIndex; + effect eEffect = GetFirstEffect(oPC); + while(GetIsEffectValid(eEffect)) + { + if(GetEffectLinkId(eEffect) == sEffectLinkID) + { + RemoveEffect(oPC, eEffect); + int nEffectIconToken = NuiFindWindow(oPC, AI_EFFECT_ICON_NUI); + if(nEffectIconToken) DelayCommand(0.0, NuiDestroy(oPC, nEffectIconToken)); + } + nIndex++; + eEffect = GetNextEffect(oPC); + } + } + } + else if(sEvent == "mousedown") + { + AssignCommand(oPC, PlaySound("gui_button")); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + } + } +} +void ai_SetWidgetButtonToCheckbox(object oPC, int nButton, object oAssociate, string sAssociateType, int nToken, string sElem) +{ + int bCheck = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + ai_SetWidgetButton(oPC, nButton, oAssociate, sAssociateType, bCheck); +} +void ai_SetAIButtonToCheckbox(object oPC, int nButton, object oAssociate, string sAssociateType, int nToken, string sElem) +{ + int bCheck = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + ai_SetAIButton(oPC, nButton, oAssociate, sAssociateType, bCheck); +} +void ai_SetLootFilterToCheckbox(object oPC, object oAssociate, int nFilterBit, int nToken, string sElem) +{ + int bCheck = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + ai_SetLootFilter(oAssociate, nFilterBit, bCheck); +} +void ai_AddAssociate(object oPC, int nToken, json jAssociate, location lLocation, int nFamiliar, int nCompanion, int nRange = 0) +{ + object oAssociate = JsonToObject(jAssociate, lLocation, OBJECT_INVALID, TRUE); + //ChangeToStandardFaction(oAssociate, STANDARD_FACTION_COMMONER); + //SetStandardFactionReputation(STANDARD_FACTION_COMMONER, 50, oAssociate); + //SetStandardFactionReputation(STANDARD_FACTION_DEFENDER, 50, oAssociate); + //SetStandardFactionReputation(STANDARD_FACTION_MERCHANT, 50, oAssociate); + //SetStandardFactionReputation(STANDARD_FACTION_HOSTILE, 0, oAssociate); + AddHenchman(oPC, oAssociate); + DeleteLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE"); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + if(nRange) SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION, nRange); + if(nFamiliar) SummonFamiliar(oAssociate); + if(nCompanion) SummonAnimalCompanion(oAssociate); +} +void ai_SetCompanionType(object oPC, object oAssociate, int nToken, int nAssociateType) +{ + if(ai_GetIsCharacter(oAssociate)) return; + SetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE", TRUE); + int nSelection; + // Need to remove the henchman before we copy them to keep factions correct. + ai_FireHenchman(oPC, oAssociate); + json jAssociate = ObjectToJson(oAssociate, TRUE); + if(nAssociateType == ASSOCIATE_TYPE_FAMILIAR) + { + nSelection = JsonGetInt(NuiGetBind(oPC, nToken, "cmb_familiar_selected")); + jAssociate = GffReplaceInt(jAssociate, "FamiliarType", nSelection); + } + else if(nAssociateType == ASSOCIATE_TYPE_ANIMALCOMPANION) + { + nSelection = JsonGetInt(NuiGetBind(oPC, nToken, "cmb_companion_selected")); + jAssociate = GffReplaceInt(jAssociate, "CompanionType", nSelection); + } + //ai_Debug("0e_nui", "916", JsonDump(jAssociate, 1)); + location lLocation = GetLocation(oAssociate); + int nFamiliar, nCompanion; + object oCompanion = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oAssociate); + if(oCompanion != OBJECT_INVALID) nFamiliar = TRUE; + oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oAssociate); + if(oCompanion != OBJECT_INVALID) nCompanion = TRUE; + SetIsDestroyable(TRUE, FALSE, FALSE, oAssociate); + DestroyObject(oAssociate); + DelayCommand(0.1, ai_AddAssociate(oPC, nToken, jAssociate, lLocation, nFamiliar, nCompanion)); +} +void ai_SetCompanionName(object oPC, object oAssociate, int nToken, int nAssociateType) +{ + if(ai_GetIsCharacter(oAssociate)) return; + SetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE", TRUE); + string sAssociateType; + string sName; + // Need to remove the henchman before we copy them to keep factions correct. + ai_FireHenchman(oPC, oAssociate); + json jAssociate = ObjectToJson(oAssociate, TRUE); + if(nAssociateType == ASSOCIATE_TYPE_FAMILIAR) + { + sName = JsonGetString(NuiGetBind(oPC, nToken, "txt_familiar_name")); + jAssociate = GffReplaceString(jAssociate, "FamiliarName", sName); + } + else if(nAssociateType == ASSOCIATE_TYPE_ANIMALCOMPANION) + { + sAssociateType = "txt_companion_name"; + sName = JsonGetString(NuiGetBind(oPC, nToken, "txt_companion_name")); + jAssociate = GffReplaceString(jAssociate, "FamiliarName", sName); + } + location lLocation = GetLocation(oAssociate); + int nFamiliar, nCompanion; + object oCompanion = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oAssociate); + if(oCompanion != OBJECT_INVALID) nFamiliar = TRUE; + oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oAssociate); + if(oCompanion != OBJECT_INVALID) nCompanion = TRUE; + SetIsDestroyable(TRUE, FALSE, FALSE, oAssociate); + DestroyObject(oAssociate); + DelayCommand(0.1, ai_AddAssociate(oPC, nToken, jAssociate, lLocation, nFamiliar, nCompanion)); +} +void ai_SetAIScript(object oPC, object oAssociate, int nToken) +{ + int nSelection = JsonGetInt(NuiGetBind(oPC, nToken, "cmb_ai_script_selected")); + if(nSelection == 0) return; + string sScript = sScript = ResManFindPrefix("ai_a_", RESTYPE_NCS, nSelection); + NuiSetBind(oPC, nToken, "txt_ai_script", JsonString(sScript)); + string sOldScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + if(sScript != sOldScript) + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + if(JsonGetType(JsonArrayGet(jAIData, 8)) == JSON_TYPE_NULL) jAIData = JsonArrayInsert(jAIData, JsonString(sScript)); + else jAIData = JsonArraySet(jAIData, 8, JsonString(sScript)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + ai_SendMessages(GetName(oAssociate) + " is now using " + sScript + " AI script!", AI_COLOR_GREEN, oPC); + } + else ai_SendMessages(GetName(oAssociate) + " is already using this script! Did not change AI script.", AI_COLOR_RED, oPC); +} +void ai_PercRangeIncrement(object oPC, object oAssociate, int nIncrement, string sAssociateType, int nToken) +{ + int nAdjustment = GetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION + "_MENU"); + nAdjustment += nIncrement; + if(nAdjustment < 8 || nAdjustment > 11) return; + SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION + "_MENU", nAdjustment); + json jAssociate = ObjectToJson(oAssociate, TRUE); + int nHenchPercRange = JsonGetInt(GffGetByte(jAssociate, "PerceptionRange")); + string sText, sInfo; + if(nAdjustment == nHenchPercRange) + { + if(nAdjustment == 8) sText = " Perception Range Short [10 meters Sight / 10 meters Listen]"; + else if(nAdjustment == 9) sText = " Perception Range Medium [20 meters Sight / 20 meters Listen]"; + else if(nAdjustment == 10) sText = " Perception Range Long [35 meters Sight / 20 meters Listen]"; + else sText = " Perception Range Default [20 meters Sight / 20 meters Listen]"; + sInfo = " "; + } + else + { + if(nAdjustment == 8) sText = " !!! Click the Perception Range button to set to short range !!!"; + else if(nAdjustment == 9) sText = " !!! Click the Perception Range button to set to medium range !!!"; + else if(nAdjustment == 10) sText = " !!! Click the Perception Range button to set to long range !!!"; + else sText = " !!! Click the Perception Range button to set to the default range !!!"; + sInfo = sText; + } + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_perc_range_tooltip", sText); + if(nToken > -1) NuiSetBind (oPC, nToken, "lbl_info_label", JsonString(sInfo)); +} +void ai_Perc_Range(object oPC, object oAssociate, int nToken, string sAssociateType) +{ + if(ai_GetIsCharacter(oAssociate)) return; + SetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE", TRUE); + int nBtnPercRange = GetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION + "_MENU"); + string sText, sText2; + float fRange = 20.0; + if(nBtnPercRange == 8) + { + sText = "short"; + sText2 = " Perception Range Short [10 meters Sight / 10 meters Listen]"; + fRange = 10.0; + } + else if(nBtnPercRange == 9) + { + sText = "medium"; + sText2 = " Perception Range Medium [20 meters Sight / 20 meters Listen]"; + } + else if(nBtnPercRange == 10) + { + sText = "long"; + sText2 = " Perception Range Long [35 meters Sight / 20 meters Listen]"; + fRange = 35.0; + } + else if(nBtnPercRange == 11) + { + sText = "default"; + sText2 = " Perception Range Default [20 meters Sight / 20 meters Listen]"; + } + SetLocalFloat(oAssociate, AI_ASSOC_PERCEPTION_DISTANCE, fRange); + SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION, nBtnPercRange); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 7, JsonInt(nBtnPercRange)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + // Need to remove the henchman before we copy them to keep factions correct. + ai_FireHenchman(oPC, oAssociate); + json jAssociate = ObjectToJson(oAssociate, TRUE); + int nHenchPercRange = JsonGetInt(GffGetByte(jAssociate, "PerceptionRange")); + if(nBtnPercRange == nHenchPercRange) + { + ai_SendMessages(GetName(oAssociate) + " already has this perception set.", AI_COLOR_YELLOW, oPC); + AddHenchman(oPC, oAssociate); + DeleteLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE"); + return; + } + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_NUI))); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_perc_range_tooltip", sText2); + ai_SendMessages(GetName(oAssociate) + " has updated their perception range to " + sText + ".", AI_COLOR_YELLOW, oPC); + location lLocation = GetLocation(oAssociate); + jAssociate = GffReplaceByte(jAssociate, "PerceptionRange", nBtnPercRange); + int nFamiliar, nCompanion; + object oCompanion = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oAssociate); + if(oCompanion != OBJECT_INVALID) nFamiliar = TRUE; + oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oAssociate); + if(oCompanion != OBJECT_INVALID) nCompanion = TRUE; + SetIsDestroyable(TRUE, FALSE, FALSE, oAssociate); + DestroyObject(oAssociate); + DelayCommand(0.1, ai_AddAssociate(oPC, nToken, jAssociate, lLocation, nFamiliar, nCompanion, nBtnPercRange)); +} +void ai_RulePercDistInc(object oPC, object oModule, int nIncrement, int nToken) +{ + int nAdjustment = GetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE) + nIncrement; + if(nAdjustment < 8 || nAdjustment > 11) return; + SetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE, nAdjustment); + string sText; + if(nAdjustment == 8) sText = " Monster perception: Short [10 Sight / 10 Listen]"; + else if(nAdjustment == 9) sText = " Monster perception: Medium [20 Sight / 20 Listen]"; + else if(nAdjustment == 10) sText = " Monster perception: Long [35 Sight / 20 Listen]"; + else sText = " Monster perception: Default [Monster's default values]"; + NuiSetBind(oPC, nToken, "lbl_perc_dist_label", JsonString(sText)); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_RULE_MON_PERC_DISTANCE, JsonInt(nAdjustment)); + ai_SetCampaignDbJson("rules", jRules); +} +json ai_AddRestrictedSpell(json jRules, int nSpell, int bRestrict = TRUE) +{ + object oModule = GetModule(); + json jRSpells = GetLocalJson(oModule, AI_RULE_RESTRICTED_SPELLS); + if(JsonGetType(jRSpells) == JSON_TYPE_NULL) jRSpells = JsonArray(); + int nIndex, nMaxIndex = JsonGetLength(jRSpells); + if(bRestrict) + { + while(nIndex < nMaxIndex) + { + if(JsonGetInt(JsonArrayGet(jRSpells, nIndex)) == nSpell) return jRules; + nIndex++; + } + jRSpells = JsonArrayInsert(jRSpells, JsonInt(nSpell)); + } + else + { + while(nIndex < nMaxIndex) + { + if(JsonGetInt(JsonArrayGet(jRSpells, nIndex)) == nSpell) + { + jRSpells = JsonArrayDel(jRSpells, nIndex); + break; + } + nIndex++; + } + } + 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", ""); + object oHenchman = JsonToObject(jHenchman, lLocation, OBJECT_INVALID, TRUE); + AddHenchman(oPC, oHenchman); + DeleteLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE"); + string sAssociateType = ai_GetAssociateType(oPC, oHenchman); + NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI)); + if(nFamiliar) SummonFamiliar(oHenchman); + if(nCompanion) SummonAnimalCompanion(oHenchman); + return oHenchman; +} + diff --git a/_module/nss/0e_nui_dm.nss b/_module/nss/0e_nui_dm.nss new file mode 100644 index 00000000..4ffd8515 --- /dev/null +++ b/_module/nss/0e_nui_dm.nss @@ -0,0 +1,700 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: 0e_nui_dm + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Menu event script + sEvent: close, click, mousedown, mouseup, watch (if bindwatch is set). +/*////////////////////////////////////////////////////////////////////////////// +#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); +void ai_SetDMAIAccessButtonToCheckbox(object oDM, int nButton, int nToken, string sElem); +void ai_SetDMAIAccessButtonToCheckbox(object oDM, int nButton, int nToken, string sElem); +void ai_RulePercDistInc(object oDM, 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); +// Adds a selected creature to the group. +void ai_SelectToGroup(object oDM, string sElem); +// Does a selected action for nGroup. +void ai_DMSelectAction(object oDM, string sElem); +// Changes if the group will run (nSpeed: 1) or walk (nSpeed: 0). +void ai_DMChangeMoveSpeed(object oDM, string sElem, int nSpeed); +void main() +{ + object oDM = NuiGetEventPlayer(); + int nToken = NuiGetEventWindow(); + string sEvent = NuiGetEventType(); + string sElem = NuiGetEventElement(); + int nIndex = NuiGetEventArrayIndex(); + string sWndId = NuiGetWindowId(oDM, nToken); + //if(AI_DEBUG) ai_Debug ("0e_nui", "58", "sWndId: " + sWndId + " sEvent: " + sEvent + " sElem: " + sElem + + // " nToken: " + IntToString(nToken) + " oPC: " + GetName(oPC)); + //WriteTimestampedLogEntry("0e_nui, 58, sWndId: " + sWndId + " sEvent: " + sEvent + " sElem: " + sElem + + // " nToken: " + IntToString(nToken) + " oDM: " + GetName(oDM)); + //************************************************************************** + string sName = ai_RemoveIllegalCharacters(GetName(oDM)); + // Watch to see if the window moves and save. + if(sElem == "window_geometry" && sEvent == "watch") + { + if(GetLocalInt(oDM, AI_NO_NUI_SAVE)) return; + SaveMenuToCampaignDb(oDM, nToken, sWndId); + } + //************************************************************************** + // Widget events. + if(sWndId == "dm" + AI_WIDGET_NUI) + { + //if(GetLocalInt(oDM, AI_NO_NUI_SAVE)) return; + if(sEvent == "click") + { + if(sElem == "btn_open_main") + { + if(IsWindowClosed(oDM, "dm" + AI_COMMAND_NUI)) ai_CreateDMCommandNUI(oDM); + IsWindowClosed(oDM, "dm" + AI_MAIN_NUI); + } + else if(sElem == "btn_camera") ai_SelectCameraView(oDM); + else if(sElem == "btn_inventory") ai_SelectOpenInventory(oDM); + else if(GetStringLeft(sElem, 13) == "btn_cmd_group") + { + ai_DMSelectAction(oDM, sElem); + } + else if(GetStringLeft(sElem, 15) == "btn_exe_plugin_") ai_Plugin_Execute(oDM, sElem, TRUE); + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + if(GetStringLeft(sElem, 13) == "btn_cmd_group") ai_DMChangeMoveSpeed(oDM, sElem, 1); + } + if(nMouseScroll == -1.0) // Scroll down + { + if(GetStringLeft(sElem, 13) == "btn_cmd_group") ai_DMChangeMoveSpeed(oDM, sElem, 0); + } + } + else if(sEvent == "mousedown") + { + int nMouseButton = JsonGetInt(JsonObjectGet(NuiGetEventPayload(), "mouse_btn")); + if(nMouseButton == NUI_MOUSE_BUTTON_RIGHT) + { + if(sElem == "btn_open_main") + { + if(IsWindowClosed(oDM, "dm" + AI_MAIN_NUI)) ai_CreateDMOptionsNUI(oDM); + } + else if(GetStringLeft(sElem, 13) == "btn_cmd_group") + { + ai_SelectToGroup(oDM, sElem); + } + } + } + } + else if(sWndId == "dm" + AI_COMMAND_NUI) + { + if(sEvent == "click") + { + if(sElem == "btn_widget_lock") + { + if(ai_GetDMWidgetButton(oDM, BTN_DM_WIDGET_LOCK)) + { + ai_SendMessages(GetName(oDM) + " AI widget unlocked.", AI_COLOR_YELLOW, oDM); + ai_SetDMWidgetButton(oDM, BTN_DM_WIDGET_LOCK, FALSE); + } + else + { + ai_SendMessages(GetName(oDM) + " AI widget locked.", AI_COLOR_YELLOW, oDM); + ai_SetDMWidgetButton(oDM, BTN_DM_WIDGET_LOCK, TRUE); + } + DelayCommand(0.0, NuiDestroy(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateDMWidgetNUI(oDM)); + } + else if(sElem == "btn_main_menu") + { + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMOptionsNUI(oDM)); + } + else if(sElem == "btn_camera") ai_SelectCameraView(oDM); + else if(sElem == "btn_inventory") ai_SelectOpenInventory(oDM); + else if(GetStringLeft(sElem, 13) == "btn_cmd_group") ai_DMSelectAction(oDM, sElem); + else if(GetStringLeft(sElem, 11) == "btn_plugin_") ai_Plugin_Execute(oDM, sElem, 1); + } + else if(sEvent == "watch") + { + if(sElem == "chbx_cmd_group1_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_GROUP1, nToken, sElem); + else if(sElem == "chbx_cmd_group2_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_GROUP2, nToken, sElem); + else if(sElem == "chbx_cmd_group3_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_GROUP3, nToken, sElem); + else if(sElem == "chbx_cmd_group4_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_GROUP4, nToken, sElem); + else if(sElem == "chbx_cmd_group5_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_GROUP5, nToken, sElem); + else if(sElem == "chbx_cmd_group6_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_GROUP6, nToken, sElem); + else if(sElem == "chbx_camera_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_CAMERA, nToken, sElem); + else if(sElem == "chbx_inventory_check") ai_SetDMWidgetButtonToCheckbox(oDM, BTN_DM_CMD_INVENTORY, nToken, sElem); + if(GetStringLeft(sElem, 12) == "chbx_plugin_" && GetStringRight(sElem, 6) == "_check") + { + int nIndex = StringToInt(GetSubString(sElem, 12, 1)); + json jPlugins = ai_GetCampaignDbJson("plugins", sName, AI_DM_TABLE); + json jPlugin = JsonArrayGet(jPlugins, nIndex); + int bCheck = JsonGetInt(NuiGetBind(oDM, nToken, sElem)); + jPlugin = JsonArraySet(jPlugin, 1, JsonBool(bCheck)); + jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); + ai_SetCampaignDbJson("plugins", jPlugins, sName, AI_DM_TABLE); + } + DelayCommand(0.0, NuiDestroy(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateDMWidgetNUI(oDM)); + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + if(GetStringLeft(sElem, 13) == "btn_cmd_group") ai_DMChangeMoveSpeed(oDM, sElem, 1); + } + if(nMouseScroll == -1.0) // Scroll down + { + if(GetStringLeft(sElem, 13) == "btn_cmd_group") ai_DMChangeMoveSpeed(oDM, sElem, 0); + } + } + else if(sEvent == "mousedown") + { + int nMouseButton = JsonGetInt(JsonObjectGet(NuiGetEventPayload(), "mouse_btn")); + if(nMouseButton == NUI_MOUSE_BUTTON_RIGHT) + { + if(GetStringLeft(sElem, 13) == "btn_cmd_group") + { + ai_SelectToGroup(oDM, sElem); + } + } + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + } + else if(nMouseScroll == -1.0) // Scroll down + { + } + } + } + //************************************************************************** + // Main AI events. + if(sWndId == "dm" + AI_MAIN_NUI) + { + if(sEvent == "click") + { + if(sElem == "btn_plugin_manager") + { + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMPluginManagerNUI(oDM)); + } + if(sElem == "btn_widget_manager") + { + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMWidgetManagerNUI(oDM)); + } + } + if(sEvent == "watch") + { + if(sElem == "txt_max_henchman") + { + int nMaxHenchmen = StringToInt(JsonGetString(NuiGetBind(oDM, nToken, sElem))); + if(nMaxHenchmen < 1) nMaxHenchmen = 1; + if(nMaxHenchmen > 12) + { + nMaxHenchmen = 12; + ai_SendMessages("The maximum henchmen for this mod is 12!", AI_COLOR_RED, oDM); + } + SetMaxHenchmen(nMaxHenchmen); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_RULE_MAX_HENCHMAN, JsonInt(nMaxHenchmen)); + ai_SetCampaignDbJson("rules", jRules); + ai_SendMessages("Maximum henchmen has been changed to " + IntToString(nMaxHenchmen), AI_COLOR_YELLOW, oDM); + } + else if(sElem == "txt_ai_difficulty") + { + int nChance = StringToInt(JsonGetString(NuiGetBind(oDM, nToken, sElem))); + if(nChance < 0) nChance = 0; + else if(nChance > 100) nChance = 100; + SetLocalInt(GetModule(), AI_RULE_AI_DIFFICULTY, nChance); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_RULE_AI_DIFFICULTY, JsonInt(nChance)); + ai_SetCampaignDbJson("rules", jRules); + } + else if(sElem == "txt_perception_distance") + { + float fDistance = StringToFloat(JsonGetString(NuiGetBind(oDM, nToken, sElem))); + if(fDistance < 10.0) fDistance = 10.0; + else if(fDistance > 60.0) fDistance = 60.0; + SetLocalFloat(GetModule(), AI_RULE_PERCEPTION_DISTANCE, fDistance); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_RULE_PERCEPTION_DISTANCE, JsonFloat(fDistance)); + ai_SetCampaignDbJson("rules", jRules); + } + else if(sElem == "txt_inc_hp") + { + int nNumber = StringToInt(JsonGetString(NuiGetBind(oDM, nToken, sElem))); + if(nNumber < 0) nNumber = 0; + else if(nNumber > 100) nNumber = 100; + SetLocalInt(GetModule(), AI_INCREASE_MONSTERS_HP, nNumber); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_INCREASE_MONSTERS_HP, JsonInt(nNumber)); + ai_SetCampaignDbJson("rules", jRules); + } + else if(GetStringLeft(sElem, 4) == "chbx") + { + object oModule = GetModule(); + int bCheck = JsonGetInt(NuiGetBind(oDM, nToken, sElem)); + json jRules = ai_GetCampaignDbJson("rules"); + if(sElem == "chbx_moral_check") + { + SetLocalInt(oModule, AI_RULE_MORAL_CHECKS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_MORAL_CHECKS, JsonInt(bCheck)); + } + else if(sElem == "chbx_buff_monsters_check") + { + SetLocalInt(oModule, AI_RULE_BUFF_MONSTERS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_BUFF_MONSTERS, JsonInt(bCheck)); + } + else if(sElem == "chbx_buff_summons_check") + { + SetLocalInt(oModule, AI_RULE_PRESUMMON, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_PRESUMMON, JsonInt(bCheck)); + } + else if(sElem == "chbx_ambush_monsters_check") + { + SetLocalInt(oModule, AI_RULE_AMBUSH, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_AMBUSH, JsonInt(bCheck)); + } + else if(sElem == "chbx_companions_check") + { + SetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_SUMMON_COMPANIONS, JsonInt(bCheck)); + } + else if(sElem == "chbx_advanced_movement_check") + { + SetLocalInt(oModule, AI_RULE_ADVANCED_MOVEMENT, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_ADVANCED_MOVEMENT, JsonInt(bCheck)); + } + else if(sElem == "chbx_ilr_check") + { + SetLocalInt(oModule, AI_RULE_ILR, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_ILR, JsonInt(bCheck)); + } + else if(sElem == "chbx_umd_check") + { + SetLocalInt(oModule, AI_RULE_ALLOW_UMD, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_ALLOW_UMD, JsonInt(bCheck)); + } + else if(sElem == "chbx_use_healingkits_check") + { + SetLocalInt(oModule, AI_RULE_HEALERSKITS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_HEALERSKITS, JsonInt(bCheck)); + } + else if(sElem == "chbx_perm_assoc_check") + { + SetLocalInt(oModule, AI_RULE_PERM_ASSOC, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_PERM_ASSOC, JsonInt(bCheck)); + } + else if(sElem == "chbx_corpses_stay_check") + { + SetLocalInt(oModule, AI_RULE_CORPSES_STAY, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_CORPSES_STAY, JsonInt(bCheck)); + } + else if(sElem == "chbx_wander_check") + { + SetLocalInt(oModule, AI_RULE_WANDER, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_CORPSES_STAY, JsonInt(bCheck)); + } + else if(sElem == "chbx_open_doors_check") + { + SetLocalInt(oModule, AI_RULE_OPEN_DOORS, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_OPEN_DOORS, JsonInt(bCheck)); + } + else if(sElem == "chbx_party_scale_check") + { + if(bCheck) + { + SetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP, GetModuleXPScale()); + ai_CheckXPPartyScale(oDM); + } + else + { + SetModuleXPScale(GetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE)); + } + SetLocalInt(oModule, AI_RULE_PARTY_SCALE, bCheck); + jRules = JsonObjectSet(jRules, AI_RULE_PARTY_SCALE, JsonInt(bCheck)); + string sText = IntToString(GetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP)); + NuiSetBind(oDM, nToken, "chbx_party_scale_tooltip", JsonString(" PEPS adjusts your XP based on party size from (" + sText + ").")); + sText = IntToString(GetModuleXPScale()); + NuiSetBind(oDM, nToken, "txt_xp_scale", JsonString(sText)); + } + else if(sElem == "chbx_darkness_check") + { + if(bCheck) + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_DARKNESS); + jRules = ai_AddRestrictedSpell(jRules, 159); + jRules = ai_AddRestrictedSpell(jRules, SPELLABILITY_AS_DARKNESS); + jRules = ai_AddRestrictedSpell(jRules, 688); // WildShape_Darkness + } + else + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_DARKNESS, FALSE); + jRules = ai_AddRestrictedSpell(jRules, 159, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELLABILITY_AS_DARKNESS, FALSE); + jRules = ai_AddRestrictedSpell(jRules, 688, FALSE); // WildShape_Darkness + } + } + else if(sElem == "chbx_dispels_check") + { + if(bCheck) + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_LESSER_DISPEL); + jRules = ai_AddRestrictedSpell(jRules, SPELL_DISPEL_MAGIC); + jRules = ai_AddRestrictedSpell(jRules, SPELL_GREATER_DISPELLING); + jRules = ai_AddRestrictedSpell(jRules, SPELL_MORDENKAINENS_DISJUNCTION); + } + else + { + jRules = ai_AddRestrictedSpell(jRules, SPELL_LESSER_DISPEL, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELL_DISPEL_MAGIC, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELL_GREATER_DISPELLING, FALSE); + jRules = ai_AddRestrictedSpell(jRules, SPELL_MORDENKAINENS_DISJUNCTION, FALSE); + } + } + else if(sElem == "chbx_timestop_check") + { + if(bCheck) jRules = ai_AddRestrictedSpell(jRules, SPELL_TIME_STOP); + else jRules = ai_AddRestrictedSpell(jRules, SPELL_TIME_STOP, FALSE); + } + ai_SetCampaignDbJson("rules", jRules); + } + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + if(nMouseScroll == 1.0) // Scroll up + { + // Follow range is only changed on non-pc's + if(sElem == "lbl_perc_dist") ai_RulePercDistInc(oDM, GetModule(), 1, nToken); + } + else if(nMouseScroll == -1.0) // Scroll down + { + // Follow range is only changed on non-pc's + if(sElem == "lbl_perc_dist") ai_RulePercDistInc(oDM, GetModule(), -1, nToken); + } + } + } + //************************************************************************** + // Plugins events. + if(sWndId == "dmai_plugin_nui") + { + string sName = ai_RemoveIllegalCharacters(GetName(oDM)); + json jPlugins = ai_GetCampaignDbJson("plugins"); + if(sEvent == "click") + { + if(sElem == "btn_load_plugins") + { + string sScript = JsonGetString(NuiGetBind (oDM, nToken, "txt_plugin")); + if(JsonGetType(JsonArrayGet(jPlugins, 0)) == JSON_TYPE_NULL) jPlugins = JsonArray(); + jPlugins = ai_Plugin_Add(oDM, jPlugins, "pi_buffing"); + jPlugins = ai_Plugin_Add(oDM, jPlugins, "pi_forcerest"); + jPlugins = ai_Plugin_Add(oDM, jPlugins, "pi_henchmen"); + jPlugins = ai_Plugin_Add(oDM, jPlugins, "pi_crafting"); + jPlugins = ai_Plugin_Add(oDM, jPlugins, "pi_mod_set"); + jPlugins = ai_Plugin_Add(oDM, jPlugins, "pi_debug"); + jPlugins = ai_Plugin_Add(oDM, jPlugins, "pi_test"); + ai_SetCampaignDbJson("plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMPluginManagerNUI(oDM)); + DelayCommand(0.0, NuiDestroy(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateDMWidgetNUI(oDM)); + } + if(sElem == "btn_check_plugins") + { + int nIndex; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + jPlugin = JsonArraySet(jPlugin, 1, JsonBool(TRUE)); + jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + ai_SetCampaignDbJson("plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMPluginManagerNUI(oDM)); + DelayCommand(0.0, NuiDestroy(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateDMWidgetNUI(oDM)); + } + if(sElem == "btn_clear_plugins") + { + int nIndex; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + jPlugin = JsonArraySet(jPlugin, 1, JsonBool(FALSE)); + jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + ai_SetCampaignDbJson("plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMPluginManagerNUI(oDM)); + DelayCommand(0.0, NuiDestroy(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateDMWidgetNUI(oDM)); + } + else if(sElem == "btn_add_plugin") + { + string sScript = JsonGetString(NuiGetBind (oDM, nToken, "txt_plugin")); + if(JsonGetType(JsonArrayGet(jPlugins, 0)) == JSON_TYPE_NULL) jPlugins = JsonArray(); + jPlugins = ai_Plugin_Add(oDM, jPlugins, sScript); + ai_SetCampaignDbJson("plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMPluginManagerNUI(oDM)); + } + else if(GetStringLeft(sElem, 18) == "btn_remove_plugin_") + { + int nIndex = StringToInt(GetStringRight(sElem, 1)); + jPlugins = JsonArrayDel(jPlugins, nIndex); + ai_SetCampaignDbJson("plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMPluginManagerNUI(oDM)); + DelayCommand(0.0, NuiDestroy(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateDMWidgetNUI(oDM)); + } + else if(GetStringLeft(sElem, 11) == "btn_plugin_") ai_Plugin_Execute(oDM, sElem, 2); + } + else if(sEvent == "watch") + { + if(GetStringLeft(sElem, 12) == "chbx_plugin_" && GetStringRight(sElem, 6) == "_check") + { + int nIndex = StringToInt(GetSubString(sElem, 12, 1)); + json jPlugin = JsonArrayGet(jPlugins, nIndex); + int bCheck = JsonGetInt(NuiGetBind(oDM, nToken, sElem)); + jPlugin = JsonArraySet(jPlugin, 1, JsonBool(bCheck)); + jPlugins = JsonArraySet(jPlugins, nIndex, jPlugin); + ai_SetCampaignDbJson("plugins", jPlugins); + DelayCommand(0.0, NuiDestroy(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI))); + DelayCommand(0.1, ai_CreateDMWidgetNUI(oDM)); + } + } + } + if(sWndId == "dm_widget_manager_nui") + { + //SendMessageToDM(oDM, "sEvent: " + sEvent + " sElem: " + sElem); + if(sEvent == "click") + { + if(sElem == "btn_clear_buttons") + { + object oModule = GetModule(); + SetLocalInt(oModule, sDMWidgetAccessVarname, 0); + SetLocalInt(oModule, sDMAIAccessVarname, 0); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, sDMWidgetAccessVarname, JsonInt(0)); + jRules = JsonObjectSet(jRules, sDMAIAccessVarname, JsonInt(0)); + ai_SetCampaignDbJson("rules", jRules); + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMWidgetManagerNUI(oDM)); + return; + } + else if(sElem == "btn_check_buttons") + { + object oModule = GetModule(); + SetLocalInt(oModule, sDMWidgetAccessVarname, 7340028); + SetLocalInt(oModule, sDMAIAccessVarname, 203423743); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, sDMWidgetAccessVarname, JsonInt(7340028)); + jRules = JsonObjectSet(jRules, sDMAIAccessVarname, JsonInt(203423743)); + ai_SetCampaignDbJson("rules", jRules); + DelayCommand(0.0, NuiDestroy(oDM, nToken)); + DelayCommand(0.1, ai_CreateDMWidgetManagerNUI(oDM)); + return; + } + SetLocalInt(oDM, "CHBX_SKIP", TRUE); + DelayCommand(2.0, DeleteLocalInt(oDM, "CHBX_SKIP")); + if(sElem == "btn_cmd_action") NuiSetBind(oDM, nToken, "chbx_cmd_action_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_ACTION))); + else if(sElem == "btn_cmd_guard") NuiSetBind(oDM, nToken, "chbx_cmd_guard_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_GUARD))); + else if(sElem == "btn_cmd_hold") NuiSetBind(oDM, nToken, "chbx_cmd_hold_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_HOLD))); + else if(sElem == "btn_cmd_attack") NuiSetBind(oDM, nToken, "chbx_cmd_attack_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_ATTACK))); + else if(sElem == "btn_cmd_follow") NuiSetBind(oDM, nToken, "chbx_cmd_follow_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_FOLLOW))); + else if(sElem == "btn_follow_target") NuiSetBind(oDM, nToken, "chbx_follow_target_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_FOLLOW_TARGET))); + else if(sElem == "btn_cmd_search") NuiSetBind(oDM, nToken, "chbx_cmd_search_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_SEARCH))); + else if(sElem == "btn_cmd_stealth") NuiSetBind(oDM, nToken, "chbx_cmd_stealth_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_STEALTH))); + else if(sElem == "btn_cmd_ai_script") NuiSetBind(oDM, nToken, "chbx_cmd_ai_script_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_AI_SCRIPT))); + else if(sElem == "btn_cmd_place_trap") NuiSetBind(oDM, nToken, "chbx_cmd_place_trap_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_PLACE_TRAP))); + else if(sElem == "btn_quick_widget") NuiSetBind(oDM, nToken, "chbx_quick_widget_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_SPELL_WIDGET))); + else if(sElem == "btn_spell_memorize") NuiSetBind(oDM, nToken, "chbx_spell_memorize_check", JsonBool(!ai_GetDMWAccessButton(BTN_DM_CMD_MEMORIZE))); + else if(sElem == "btn_buff_short") NuiSetBind(oDM, nToken, "chbx_buff_short_check", JsonBool(!ai_GetDMWAccessButton(BTN_BUFF_SHORT))); + else if(sElem == "btn_buff_long") NuiSetBind(oDM, nToken, "chbx_buff_long_check", JsonBool(!ai_GetDMWAccessButton(BTN_BUFF_LONG))); + else if(sElem == "btn_buff_all") NuiSetBind(oDM, nToken, "chbx_buff_all_check", JsonBool(!ai_GetDMWAccessButton(BTN_BUFF_ALL))); + else if(sElem == "btn_buff_rest") NuiSetBind(oDM, nToken, "chbx_buff_rest_check", JsonBool(!ai_GetDMWAccessButton(BTN_BUFF_REST))); + else if(sElem == "btn_jump_to") NuiSetBind(oDM, nToken, "chbx_jump_to_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_JUMP_TO))); + else if(sElem == "btn_ghost_mode") NuiSetBind(oDM, nToken, "chbx_ghost_mode_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_GHOST_MODE))); + else if(sElem == "btn_camera") NuiSetBind(oDM, nToken, "chbx_camera_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_CAMERA))); + else if(sElem == "btn_inventory") NuiSetBind(oDM, nToken, "chbx_inventory_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_INVENTORY))); + else if(sElem == "btn_familiar") NuiSetBind(oDM, nToken, "chbx_familiar_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_FAMILIAR))); + else if(sElem == "btn_companion") NuiSetBind(oDM, nToken, "chbx_companion_check", JsonBool(!ai_GetDMWAccessButton(BTN_CMD_COMPANION))); + else if(sElem == "btn_ai") NuiSetBind(oDM, nToken, "chbx_ai_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_FOR_PC))); + else if(sElem == "btn_quiet") NuiSetBind(oDM, nToken, "chbx_quiet_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_REDUCE_SPEECH))); + else if(sElem == "btn_ranged") NuiSetBind(oDM, nToken, "chbx_ranged_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_USE_RANGED))); + else if(sElem == "btn_search") NuiSetBind(oDM, nToken, "chbx_search_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_USE_SEARCH))); + else if(sElem == "btn_stealth") NuiSetBind(oDM, nToken, "chbx_stealth_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_USE_STEALTH))); + else if(sElem == "btn_open_door") NuiSetBind(oDM, nToken, "chbx_open_door_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_OPEN_DOORS))); + else if(sElem == "btn_traps") NuiSetBind(oDM, nToken, "chbx_traps_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_REMOVE_TRAPS))); + else if(sElem == "btn_pick_locks") NuiSetBind(oDM, nToken, "chbx_pick_locks_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_PICK_LOCKS))); + else if(sElem == "btn_bash_locks") NuiSetBind(oDM, nToken, "chbx_bash_locks_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_BASH_LOCKS))); + else if(sElem == "btn_magic_level") NuiSetBind(oDM, nToken, "chbx_magic_level_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_MAGIC_LEVEL))); + else if(sElem == "btn_spontaneous") NuiSetBind(oDM, nToken, "chbx_spontaneous_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_NO_SPONTANEOUS))); + else if(sElem == "btn_magic") NuiSetBind(oDM, nToken, "chbx_magic_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_NO_MAGIC_USE))); + else if(sElem == "btn_magic_items") NuiSetBind(oDM, nToken, "chbx_magic_items_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_NO_MAGIC_ITEM_USE))); + else if(sElem == "btn_def_magic") NuiSetBind(oDM, nToken, "chbx_def_magic_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_DEF_MAGIC_USE))); + else if(sElem == "btn_off_magic") NuiSetBind(oDM, nToken, "chbx_off_magic_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_OFF_MAGIC_USE))); + else if(sElem == "btn_heal_out") NuiSetBind(oDM, nToken, "chbx_heal_out_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_HEAL_OUT))); + else if(sElem == "btn_heal_in") NuiSetBind(oDM, nToken, "chbx_heal_in_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_HEAL_IN))); + else if(sElem == "btn_heals_onoff") NuiSetBind(oDM, nToken, "chbx_heals_onoff_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_STOP_SELF_HEALING))); + else if(sElem == "btn_healp_onoff") NuiSetBind(oDM, nToken, "chbx_healp_onoff_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_STOP_PARTY_HEALING))); + else if(sElem == "btn_loot") NuiSetBind(oDM, nToken, "chbx_loot_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_LOOT))); + else if(sElem == "btn_ignore_assoc") NuiSetBind(oDM, nToken, "chbx_ignore_assoc_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_IGNORE_ASSOCIATES))); + else if(sElem == "btn_ignore_traps") NuiSetBind(oDM, nToken, "chbx_ignore_traps_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_IGNORE_TRAPS))); + else if(sElem == "btn_perc_range") NuiSetBind(oDM, nToken, "chbx_perc_range_check", JsonBool(!ai_GetDMAIAccessButton(BTN_AI_PERC_RANGE))); + } + if(sEvent == "watch") + { + if(GetLocalInt(oDM, "CHBX_SKIP")) return; + if(sElem == "chbx_cmd_action_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_ACTION, nToken, sElem); + else if(sElem == "chbx_cmd_guard_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_GUARD, nToken, sElem); + else if(sElem == "chbx_cmd_hold_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_HOLD, nToken, sElem); + else if(sElem == "chbx_cmd_attack_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_ATTACK, nToken, sElem); + else if(sElem == "chbx_cmd_follow_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_FOLLOW, nToken, sElem); + else if(sElem == "chbx_follow_target_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_FOLLOW_TARGET, nToken, sElem); + else if(sElem == "chbx_cmd_search_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_SEARCH, nToken, sElem); + else if(sElem == "chbx_cmd_stealth_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_STEALTH, nToken, sElem); + else if(sElem == "chbx_cmd_ai_script_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_AI_SCRIPT, nToken, sElem); + else if(sElem == "chbx_cmd_place_trap_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_PLACE_TRAP, nToken, sElem); + else if(sElem == "chbx_quick_widget_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_SPELL_WIDGET, nToken, sElem); + else if(sElem == "chbx_spell_memorize_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_DM_CMD_MEMORIZE, nToken, sElem); + else if(sElem == "chbx_buff_short_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_BUFF_SHORT, nToken, sElem); + else if(sElem == "chbx_buff_long_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_BUFF_LONG, nToken, sElem); + else if(sElem == "chbx_buff_all_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_BUFF_ALL, nToken, sElem); + else if(sElem == "chbx_buff_rest_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_BUFF_REST, nToken, sElem); + else if(sElem == "chbx_jump_to_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_JUMP_TO, nToken, sElem); + else if(sElem == "chbx_ghost_mode_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_GHOST_MODE, nToken, sElem); + else if(sElem == "chbx_camera_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_CAMERA, nToken, sElem); + else if(sElem == "chbx_inventory_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_INVENTORY, nToken, sElem); + else if(sElem == "chbx_familiar_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_FAMILIAR, nToken, sElem); + else if(sElem == "chbx_companion_check") ai_SetDMWAccessButtonToCheckbox(oDM, BTN_CMD_COMPANION, nToken, sElem); + else if(sElem == "chbx_ai_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_FOR_PC, nToken, sElem); + else if(sElem == "chbx_quiet_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_REDUCE_SPEECH, nToken, sElem); + else if(sElem == "chbx_ranged_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_USE_RANGED, nToken, sElem); + else if(sElem == "chbx_search_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_USE_SEARCH, nToken, sElem); + else if(sElem == "chbx_stealth_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_USE_STEALTH, nToken, sElem); + else if(sElem == "chbx_open_door_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_OPEN_DOORS, nToken, sElem); + else if(sElem == "chbx_traps_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_REMOVE_TRAPS, nToken, sElem); + else if(sElem == "chbx_pick_locks_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_PICK_LOCKS, nToken, sElem); + else if(sElem == "chbx_bash_locks_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_BASH_LOCKS, nToken, sElem); + else if(sElem == "chbx_magic_level_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_MAGIC_LEVEL, nToken, sElem); + else if(sElem == "chbx_spontaneous_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_NO_SPONTANEOUS, nToken, sElem); + else if(sElem == "chbx_magic_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_NO_MAGIC_USE, nToken, sElem); + else if(sElem == "chbx_magic_items_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_NO_MAGIC_ITEM_USE, nToken, sElem); + else if(sElem == "chbx_def_magic_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_DEF_MAGIC_USE, nToken, sElem); + else if(sElem == "chbx_off_magic_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_OFF_MAGIC_USE, nToken, sElem); + else if(sElem == "chbx_heal_out_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_HEAL_OUT, nToken, sElem); + else if(sElem == "chbx_heal_in_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_HEAL_IN, nToken, sElem); + else if(sElem == "chbx_heals_onoff_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_STOP_SELF_HEALING, nToken, sElem); + else if(sElem == "chbx_healp_onoff_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_STOP_PARTY_HEALING, nToken, sElem); + else if(sElem == "chbx_loot_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_LOOT, nToken, sElem); + else if(sElem == "chbx_ignore_assoc_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_IGNORE_ASSOCIATES, nToken, sElem); + else if(sElem == "chbx_ignore_traps_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_IGNORE_TRAPS, nToken, sElem); + else if(sElem == "chbx_perc_range_check") ai_SetDMAIAccessButtonToCheckbox(oDM, BTN_AI_PERC_RANGE, nToken, sElem); + } + } +} +void ai_SetDMWidgetButtonToCheckbox(object oDM, int nButton, int nToken, string sElem) +{ + int bCheck = JsonGetInt(NuiGetBind(oDM, nToken, sElem)); + ai_SetDMWidgetButton(oDM, nButton, bCheck); +} +void ai_SetDMWAccessButtonToCheckbox(object oDM, int nButton, int nToken, string sElem) +{ + int bCheck = JsonGetInt(NuiGetBind(oDM, nToken, sElem)); + ai_SetDMWAccessButton(nButton, bCheck); +} +void ai_SetDMAIAccessButtonToCheckbox(object oDM, int nButton, int nToken, string sElem) +{ + int bCheck = JsonGetInt(NuiGetBind(oDM, nToken, sElem)); + ai_SetDMAIAccessButton(nButton, bCheck); +} +void ai_RulePercDistInc(object oDM, object oModule, int nIncrement, int nToken) +{ + int nAdjustment = GetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE) + nIncrement; + if(nAdjustment < 8 || nAdjustment > 11) return; + SetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE, nAdjustment); + string sText; + if(nAdjustment == 8) sText = " Monster perception: Short [10 Sight / 10 Listen]"; + else if(nAdjustment == 9) sText = " Monster perception: Medium [20 Sight / 20 Listen]"; + else if(nAdjustment == 10) sText = " Monster perception: Long [35 Sight / 20 Listen]"; + else sText = " Monster perception: Default [Monster's default values]"; + NuiSetBind(oDM, nToken, "lbl_perc_dist_label", JsonString(sText)); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_RULE_MON_PERC_DISTANCE, JsonInt(nAdjustment)); + ai_SetCampaignDbJson("rules", jRules); +} +json ai_AddRestrictedSpell(json jRules, int nSpell, int bRestrict = TRUE) +{ + object oModule = GetModule(); + json jRSpells = GetLocalJson(oModule, AI_RULE_RESTRICTED_SPELLS); + int nIndex, nMaxIndex = JsonGetLength(jRSpells); + if(bRestrict) + { + while(nIndex < nMaxIndex) + { + if(JsonGetInt(JsonArrayGet(jRSpells, nIndex)) == nSpell) return jRules; + nIndex++; + } + jRSpells = JsonArrayInsert(jRSpells, JsonInt(nSpell)); + } + else + { + while(nIndex < nMaxIndex) + { + if(JsonGetInt(JsonArrayGet(jRSpells, nIndex)) == nSpell) + { + jRSpells = JsonArrayDel(jRSpells, nIndex); + break; + } + nIndex++; + } + } + SetLocalJson(oModule, AI_RULE_RESTRICTED_SPELLS, jRSpells); + return JsonObjectSet(jRules, AI_RULE_RESTRICTED_SPELLS, jRSpells); +} +void ai_SelectToGroup(object oDM, string sElem) +{ + string sGroup = GetStringRight(sElem, 1); + SetLocalString(oDM, AI_TARGET_MODE, "DM_SELECT_GROUP" + sGroup); + ai_SendMessages("Select a creature to add to group " + sGroup + ". Selecting yourself will clear group1.", AI_COLOR_YELLOW, oDM); + EnterTargetingMode(oDM, OBJECT_TYPE_CREATURE, MOUSECURSOR_PICKUP, MOUSECURSOR_PICKUP_DOWN); +} +void ai_DMSelectAction(object oDM, string sElem) +{ + string sGroup = GetStringRight(sElem, 1); + SetLocalString(oDM, AI_TARGET_MODE, "DM_ACTION_GROUP" + sGroup); + ai_SendMessages(GetName(oDM) + " select an action for group" + sGroup + ".", AI_COLOR_YELLOW, oDM); + EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_DMChangeMoveSpeed(object oDM, string sElem, int nSpeed) +{ + string sGroup = GetStringRight(sElem, 1); + json jGroup = GetLocalJson(oDM, "DM_GROUP" + sGroup); + if(JsonGetType(jGroup) == JSON_TYPE_NULL) + { + ai_SendMessages("This group does not contain any creatures!", AI_COLOR_RED, oDM); + return; + } + jGroup = JsonArraySet(jGroup, 0, JsonInt(nSpeed)); + SetLocalJson(oDM, "DM_GROUP" + sGroup, jGroup); + object oLeader = GetObjectByUUID(JsonGetString(JsonArrayGet(jGroup, 1))); + string sName = GetName(oLeader); + string sText = " " + sName + "'s group"; + if(nSpeed == 0) sText += " [Walk]"; + else sText += " [Run]"; + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText)); + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText)); +} diff --git a/_module/nss/0e_onclientload.nss b/_module/nss/0e_onclientload.nss new file mode 100644 index 00000000..041d49ff --- /dev/null +++ b/_module/nss/0e_onclientload.nss @@ -0,0 +1,23 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_onclientload + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnClientLoad script; + This will fire when the client is loading. + + If you have your own OnClientLoad event script just take the below + script lines and add them into your OnClientLoad script. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_menus_dm" +#include "0i_module" +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); + } +} diff --git a/_module/nss/0e_player_target.nss b/_module/nss/0e_player_target.nss new file mode 100644 index 00000000..16b83fb6 --- /dev/null +++ b/_module/nss/0e_player_target.nss @@ -0,0 +1,154 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: 0e_player_target + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + OnPlayerTarget event script + Used to allow player targeting while passing any module player targeting + script through to work as intended. + + We Use a string variable upon the player using the targeting mode to define the + action of the target. + AI_TARGET_MODE is the constant used. + AI_TARGET_ASSOCIATE is the associate that triggered the target mode. +/*////////////////////////////////////////////////////////////////////////////// +#include "0i_player_target" +void main() +{ + object oPC = GetLastPlayerToSelectTarget(); + // Get any plugin target scripts and run it instead of this one. + string sPluginTargetScript = GetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT); + if(sPluginTargetScript != "") + { + DeleteLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT); + ExecuteScript(sPluginTargetScript, oPC); + // Remove the plugin script as it must be set each time the plugin uses the target event. + } + else + { + // Get the targeting mode data + object oTarget = GetTargetingModeSelectedObject(); + vector vTarget = GetTargetingModeSelectedPosition(); + location lLocation = Location(GetArea(oPC), vTarget, GetFacing(oPC)); + object oAssociate = GetLocalObject(oPC, AI_TARGET_ASSOCIATE); + string sTargetMode = GetLocalString(oPC, AI_TARGET_MODE); + // ********************* Exiting Target Actions ************************ + // If the user manually exited targeting mode without selecting a target, return + if(!GetIsObjectValid(oTarget) && vTarget == Vector()) + { + 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); + } + 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) == "") + { + if(GetLocalInt(oPC, sGhostModeVarname)) + { + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); + } + } + 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_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); + } + ExecuteScript("nw_ch_ac1", oAssociate); + } + } + else if(sTargetMode == "ASSOCIATE_GET_TRAP") + { + ai_SendMessages(GetName(oAssociate) + " has exited selecing a trap!", AI_COLOR_YELLOW, oPC); + } + else if(sTargetMode == "ASSOCIATE_PLACE_TRAP") + { + ai_SendMessages(GetName(oAssociate) + " has exited placing the trap!", AI_COLOR_YELLOW, oPC); + } + 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; + } + // ************************* Targeted Actions ************************** + else + { + // 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); + } + 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)); + } + 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)); + } + 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)); + } + 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)); + } + 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); + } + else if(sTargetMode == "DM_SELECT_OPEN_INVENTORY") + { + if(LineOfSightObject(oPC, oTarget)) + { + OpenInventory(oTarget, oPC); + 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); + } + else if(GetStringLeft(sTargetMode, 15) == "DM_SELECT_GROUP") + { + ai_AddToGroup(oPC, oTarget, sTargetMode); + } + else if(GetStringLeft(sTargetMode, 15) == "DM_ACTION_GROUP") + { + ai_DMAction(oPC, oTarget, lLocation, sTargetMode); + } + // Get saved module player target script and execute it for pass through compatibility. + string sModuleTargetScript = GetLocalString(GetModule(), AI_MODULE_TARGET_EVENT); + ExecuteScript(sModuleTargetScript); + } + } +} diff --git a/_module/nss/0e_prc_ch_events.nss b/_module/nss/0e_prc_ch_events.nss new file mode 100644 index 00000000..3028e9c8 --- /dev/null +++ b/_module/nss/0e_prc_ch_events.nss @@ -0,0 +1,78 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0e_prc_ch_events +//////////////////////////////////////////////////////////////////////////////// + associate event handler while using the PRC. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +#include "x0_i0_assoc" +void main() +{ + object oCreature = OBJECT_SELF; + int nEvent = GetCurrentlyRunningEvent(); + //WriteTimestampedLogEntry("0e_prc_ch_events [13] " + GetName(oCreature) + " nEvent: " + IntToString(nEvent)); + switch (nEvent) + { + case EVENT_SCRIPT_CREATURE_ON_HEARTBEAT: + { + if(GetLocalInt(oCreature, "CohortID")) ExecuteScript("prc_ai_coh_hb"); + ExecuteScript("nw_ch_ac1", oCreature); + ExecuteScript("prc_npc_hb", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_NOTICE: + { + ExecuteScript("nw_ch_ac2", oCreature); + ExecuteScript("prc_npc_percep", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_DIALOGUE: + { + //if(GetLocalInt(oCreature, "CohortID")) ExecuteScript("prc_ai_coh_conv"); + ExecuteScript("nw_ch_ac4", oCreature); + //ExecuteScript("prc_npc_conv", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED: + { + ExecuteScript("nw_ch_ac5", oCreature); + ExecuteScript("prc_npc_physatt", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_DAMAGED: + { + ExecuteScript("nw_ch_ac6", oCreature); + ExecuteScript("prc_npc_damaged", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT: + { + ExecuteScript("nw_ch_acb", oCreature); + ExecuteScript("prc_npc_spellat", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND: + { + ExecuteScript("nw_ch_ac3", oCreature); + ExecuteScript("prc_npc_combat", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR: + { + ExecuteScript("nw_ch_ace", oCreature); + ExecuteScript("prc_npc_blocked", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_RESTED: + { + ExecuteScript("nw_ch_aca", oCreature); + //ExecuteScript("prc_npc_rested", oCreature); + break; + } + case EVENT_SCRIPT_CREATURE_ON_DISTURBED: + { + ExecuteScript("nw_ch_ac8", oCreature); + ExecuteScript("prc_npc_disturb", oCreature); + break; + } + } +} diff --git a/_module/nss/0i_actions.nss b/_module/nss/0i_actions.nss new file mode 100644 index 00000000..7d7d2d75 --- /dev/null +++ b/_module/nss/0i_actions.nss @@ -0,0 +1,2325 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_actions +////////////////////////////////////////////////////////////////////////////////////////////////////// + Include scripts for action in and out of combat. + + Detect Mode: + Passive(default) mode + * Trap detection radius: 5ft + * Trap detection rate: every 6 seconds + * Trap detection roll: d20 + 1/2 skill + * Spot/Listen roll: d10 + 1/2 skill + + Active(Detect) mode + * Trap detection radius: 10ft + * Trap detection rate: every 3 seconds + * Trap detection roll: d20 + skill + * Spot/Listen roll: d20 + skill + + Stealth checks + * Player detects stealth: 5 times per second. + * Player rolls for hide/move silently & spot/listen: every 6 seconds. + * NPC detects stealth: 4 seconds + * NPC rolls for hide/move silently & spot/listen: every 6 seconds. + + Listen/Move Silently: + * Cannot detect silenced creatures. + * Cannot detect sanctuaried creatures. + * Can only detect invisible (or when your blind) creatures within max attack range. + * Listen checks are made each round for success and failur. + * Outdoors: Objects between you and the target gives a +5 DC for every 40cm of thickness. + * Indoors: No Line of sight and the target is within 40 meters gives a +2 DC. + * +10 DC in combat for the target. + * +5 DC if the target is standing still. + * -5 DC if the listener is standing still. + * +1 DC for every 3 meters of distance to the target. + * Relative size modifiers for both: Tiny +8, Small +4, Medium 0, Larget -4, Huge -8. + * Favored enemy bonuses. + + Spot/Hide: + * Cannot spot invisible creatures. + * Cannot spot any creatures while blinded. + * Night time: Spotter has not light or darkvision +5 DC. + * Night time: Target has a light no them -10 DC. + * +5 DC if target is behind the spotter. + * +10 DC if the spotter are in combat. + * +5 DC if the target is standing still. + * -5 DC if the spotter is standing still. + * Relative size modifiers for both: Tiny +8, Small +4, Medium 0, Larget -4, Huge -8. + * Favored enemy bonuses. + +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_talents" +#include "x0_inc_henai" +#include "X0_I0_ANIMS" +// Chooses an action in combat and executes it for oCreature that is an associate. +void ai_DoAssociateCombatRound(object oCreature, object oTarget = OBJECT_INVALID); +// Sets variables and states for oAssociate to start combat. +void ai_StartAssociateCombat(object oAssociate, object oTarget = OBJECT_INVALID); +// Chooses an action in combat and executes it for oCreature that is a monster. +void ai_DoMonsterCombatRound(object oCreature); +// Sets variables and states for oMonster to start combat. +void ai_StartMonsterCombat(object oMonster); +// Return the distance that is set for how close we should follow our master. +float ai_GetFollowDistance(object oCreature); +// Returns TRUE if the caller's distance is greater than fDistance from who they +// are following. Unless they are cowardly or in stand ground mode. +// This will also force the caller to move towards them. +int ai_StayClose(object oCreature); +// Returns TRUE if oCreature becomes invisible or hides. +int ai_TryToBecomeInvisible(object oCreature); +// Returns TRUE if oCreature continues to bash a door. +int ai_BashDoorCheck(object oCreature); +// Returns TRUE if we find an hidden creature within battle and do an action. +// If oCreature is too far away they will run upto 14 meters of the invisible creature. +// If oCreature is close they will attempt to cast a spell or search for them. +// bMonster needs to be set for monsters otherwise we do associate perception checks. +// fRange is how close we want to get to hidden targets. +int ai_SearchForHiddenCreature(object oCreature, int bMonster, object oHidden = OBJECT_INVALID, float fRange = 1.0); +// Returns TRUE if oCreature fails a moral check. +// We only make moral checks once we are below AI_HEALTH_WOUNDED health percent. +// If we are at AI_HEALTH_BLOODY hp percent then add + AI_MORAL_INC_DC to the Check. +int ai_MoralCheck(object oCreature); +// Returns TRUE if oCreature is in and nSpell is a dangerous Area Of Effect. +// Used in the on spell cast at scripts. [nw_c2_defaultb and nw_ch_acb]. +int ai_GetInAOEReaction(object oCreature, object oCaster, int nSpell); +// Have the associate speak a random voice from VOICE_CHAT_*. +// nRoll is the number to roll. If nRoll is 0 then it will SpeakString(sVoiceChatArray); +// sVoiceChatArray is an array of VOICE_CHAT_* numbers over nRoll. +// example(4, ":3:4:8:7:") will roll a d4() picking from 3,4,8,7 of VOICE_CHAT_*. +// if nRoll is higher than the number of VOICE_CHAT_* then it will not speak. +void ai_HaveCreatureSpeak(object oCreature, int nRoll, string sVoiceChatArray, int bImportant = FALSE); +// Returns if a spell talent was used. +// This is a common set of AI scripts ran on associate spell casters. +int ai_CheckForAssociateSpellTalent(object oAssociate, int nInMelee, int nMaxLevel, int nRound = 0); +// Targets the best creature oCreature it can see. +// This checks all physcal attack talents starting with ranged attacks then melee. +// Using TALENT_CATEGORY_HARMFUL_MELEE [22] talents. +// If no talents are used it will do either a ranged attack or a melee attack. +void ai_DoPhysicalAttackOnBest(object oCreature, int nInMelee, int bAlwaysAtk = TRUE); +// Targets the nearest creature oCreature it can see. +// This checks all physcal attack talents starting with ranged attacks then melee. +// Using TALENT_CATEGORY_HARMFUL_MELEE [22] talents. +// If no talents are used it will do either a ranged attack or a melee attack. +void ai_DoPhysicalAttackOnNearest(object oCreature, int nInMelee, int bAlwaysAtk = TRUE); +// Targets the weakest creature oCreature can see. +// This checks all physcal attack talents starting with ranged attacks then melee. +// Using TALENT_CATEGORY_HARMFUL_MELEE [22] talents. +// If no talents are used it will do either a ranged attack or a melee attack. +void ai_DoPhysicalAttackOnLowestCR(object oCreature, int nInMelee, int bAlwaysAtk = TRUE); +// Returns TRUE if they equip a melee weapon, FALSE if they don't. +// This also calls for the next combat round. +int ai_InCombatEquipBestMeleeWeapon(object oCreature); +// Returns TRUE if they equip a ranged weapon, FALSE if they don't. +// This also calls for the next combat round. +int ai_InCombatEquipBestRangedWeapon(object oCreature); +// Action wrapper for ai_TryHealing. +void ai_ActionTryHealing(object oCreature, object oTarget); +// Returns TRUE if oCreature heals oTarget. +// This uses an action and must use AssignCommand or OBJECT_SELF is the caster! +int ai_TryHealing(object oCreature, object oTarget, int bForce = FALSE); +// oCreature will move into the area looking for creatures. +void ai_ScoutAhead(object oCreature); +// Have oCreature search one object, may continue from that object. +void ai_SearchObject(object oCreature, object oObject, object oMaster, int bOnce = FALSE); +// Returns TRUE if oCreature disarms oTrap. +// bForce if TRUE, oCreature will try to disarm the trap even if they have tried before. +int ai_ReactToTrap(object oCreature, object oTrap, int bForce = FALSE); +// Returns TRUE if oCreature opens oLocked object. +// This will make oCreature open oLocked either by picking or casting a spell. +// bForce if TRUE, oCreature will try to pick the lock even if they have tried before. +int ai_AttemptToByPassLock(object oCreature, object oLocked, int bForce = FALSE); +// Returns TRUE if oCreature opens oDoor. +// bForce if TRUE, oCreature will try to open the door even if they have tried before. +int ai_AttemptToOpenDoor(object oCreature, object oDoor, int bForce = FALSE); +// Action for Checking nearby objects for traps, locks and loot. +void ai_ActionCheckNearbyObjects(object oCreature); +// oCreature will check nearby objects and see what they should do based upon +// selected actions by the player. +int ai_CheckNearbyObjects(object oCreature); +// Used to determine special behaviors for oCeature. +void ai_DetermineSpecialBehavior(object oCreature); +// The target object flees to the specified way point and then destroys itself, +// to be respawned at a later point. For unkillable sign post characters +// who are not meant to fight back. +void ai_ActivateFleeToExit(object oCreature); +// Returns TRUE if oCreature should flee to an exit. +int ai_GetFleeToExit(object oCreature); +// Does random animation in a close distance for creatures. +void ai_AmbientAnimations(); + +void ai_DoAssociateCombatRound(object oCreature, object oTarget = OBJECT_INVALID) +{ + 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); + // Setup the combat state for this round of combat. + object oNearestEnemy = ai_SetCombatState(oCreature); + // If we are in standground mode we only fight if the enemy is near us. + if(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND) && + ai_GetEnemyAttackingMe(oCreature) == OBJECT_INVALID) oNearestEnemy = OBJECT_INVALID; + // If we found an Enemy or we have a Target then continue into the combat round. + if(oNearestEnemy != OBJECT_INVALID || oTarget != OBJECT_INVALID) + { + // In combat we should stop searching. + if(GetActionMode(oCreature, ACTION_MODE_DETECT) && !GetHasFeat(FEAT_KEEN_SENSE)) + { + SetActionMode(oCreature, ACTION_MODE_DETECT, FALSE); + } + ai_SetCombatRound(oCreature); + string sAI = GetLocalString(oCreature, AI_COMBAT_SCRIPT); + if(AI_DEBUG) ai_Debug("0i_actions", "167", " AI not Coward/Peaceful: " + + IntToString(sAI != "ai_coward" && sAI != "ai_a_peaceful")); + // If we are using a normal AI script and are polymorphed we should use + // 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) + { + if(ai_GetIsHidden(oCreature)) + { + ai_Debug("0i_actions", "179", "We are hidden!" + + " Can they see us? " + IntToString(ai_GetNearestIndexThatSeesUs(oCreature))); + } + } + if(GetAppearanceType(oCreature) != ai_GetNormalAppearance(oCreature)) + { + sAI = "ai_a_polymorphed"; + } + 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) + " **********"); + if(AI_DEBUG) ai_Debug("0i_actions", "191", "********** " + sAI + " **********"); + ai_ClearCreatureActions(); + if(AI_DEBUG) ai_Counter_Start(); + // Execute this creatures AI routine. + ExecuteScript(sAI, oCreature); + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + " has finalized round action."); + return; + } + // We have exhausted our check for an enemy. Combat is over. + if(AI_DEBUG) ai_Debug("0i_actions", "200", "---------- " + GetName (OBJECT_SELF) + "'s combat has ended! ----------"); + ai_ClearCombatState(oCreature); + // Run the heartbeat script so we start doing our actions out of combat. + ExecuteScript("nw_ch_ac1", oCreature); +} +void ai_StartAssociateCombat(object oAssociate, object oTarget = OBJECT_INVALID) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "217", "---------- " + GetName(oAssociate) + " is starting combat! ----------"); + ai_SetCreatureTalents(oAssociate, FALSE); + ai_CheckXPPartyScale(oAssociate); + ai_DoAssociateCombatRound(oAssociate, oTarget); +} +void ai_DoMonsterCombatRound(object oMonster) +{ + object oNearestEnemy = ai_SetCombatState(oMonster); + if(oNearestEnemy != OBJECT_INVALID) + { + if(GetActionMode(oMonster, ACTION_MODE_DETECT) && !GetHasFeat(FEAT_KEEN_SENSE, oMonster)) + SetActionMode(oMonster, ACTION_MODE_DETECT, FALSE); + ai_SetCombatRound(oMonster); + string sAI = GetLocalString(oMonster, AI_COMBAT_SCRIPT); + if(sAI != "ai_coward") + { + if(GetAppearanceType(oMonster) != ai_GetNormalAppearance(oMonster)) + { + sAI = "ai_polymorphed"; + } + 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) + " **********"); + if(AI_DEBUG) ai_Debug("0i_actions", "231", "********** " + sAI + " **********"); + // We clear actions here and setup multiple actions to the queue for oCreature. + ai_ClearCreatureActions(); + ai_Counter_Start(); + ExecuteScript(sAI, oMonster); + ai_Counter_End(GetName(oMonster) + " is ending round calculations."); + return; + } + // Check to see if we just didn't see the enemies. + if(GetLocalInt(oMonster, AI_ENEMY_NUMBERS) && + ai_SearchForHiddenCreature(oMonster, TRUE)) return; + // We have exhausted our check for an enemy. Combat is over. + ai_EndCombatRound(oMonster); + ai_ClearCombatState(oMonster); + // Run the heartbeat script so we start doing our actions out of combat. + ExecuteScript("nw_c2_default1", oMonster); + if(AI_DEBUG) ai_Debug("0i_actions", "247", GetName(oMonster) + "'s combat has ended!"); + return; +} +void ai_StartMonsterCombat(object oMonster) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "264", "---------- " + GetName(oMonster) + " is starting combat! ----------"); + ai_SetCreatureTalents(oMonster, TRUE); + ai_DoMonsterCombatRound(oMonster); +} +float ai_GetFollowDistance(object oCreature) +{ + // Also check for size of creature and adjust based on that. + float fDistance = StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oCreature))); + return GetLocalFloat(oCreature, AI_FOLLOW_RANGE) + fDistance; +} +int ai_StayClose(object oCreature) +{ + if(ai_GetIsCharacter(oCreature) || + ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND) || + GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_a_peaceful" || + GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_coward") return FALSE; + object oMaster = GetMaster(oCreature); + // We stay within our perception range of who we are following. + float fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); + if(fPerceptionDistance == 0.0) + { + fPerceptionDistance = GetLocalFloat(oMaster, AI_ASSOC_PERCEPTION_DISTANCE); + if(fPerceptionDistance == 0.0) fPerceptionDistance = 20.0; + } + object oTarget = GetLocalObject(oCreature, AI_FOLLOW_TARGET); + if(oTarget == OBJECT_INVALID) oTarget = oMaster; + if(AI_DEBUG) ai_Debug("0i_associates", "214", "Distance from who we are following in combat." + + " oFollowing: " + FloatToString(GetDistanceBetween(oTarget, oCreature), 0, 2) + " fPerceptionDistance: " + FloatToString(fPerceptionDistance, 0, 2)); + if(GetDistanceBetween(oTarget, oCreature) < fPerceptionDistance) return FALSE; + ai_ClearCreatureActions(); + if(AI_DEBUG) ai_Debug("0i_associates", "218", "We are too far away! Move back to our master."); + ActionMoveToObject(oTarget, TRUE, ai_GetFollowDistance(oCreature)); + return TRUE; +} +int ai_TryToBecomeInvisible(object oCreature) +{ + // If we are invisible then we don't need to check this. + if(!ai_GetIsHidden(oCreature)) return FALSE; + // If we are not invisible lets try. + int nDarkness; + if(GetHasSpell(SPELL_DARKNESS, oCreature) && ai_GetHasEffectType(oCreature, EFFECT_TYPE_ULTRAVISION)) nDarkness = TRUE; + if(GetHasSpell(SPELL_IMPROVED_INVISIBILITY, oCreature) || GetHasSpell(SPELL_INVISIBILITY, oCreature) || + GetHasSpell(SPELL_INVISIBILITY_SPHERE, oCreature) ||(nDarkness) || + GetHasSpell(SPELL_SANCTUARY, oCreature) || GetHasSpell(SPELL_ETHEREALNESS, oCreature) || + GetHasSpell(799/*SPELLABILITY_VAMPIRE_INVISIBILITY*/) || + GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT, oCreature) == TRUE) + { + // This bit ported directly from Jasperre + // Can anyone see me?(has spell effects of X) + // The point of this is to see if its even worthwhile to go invisbile + // or will it be immediately dispeled. + object oSeeMe = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oCreature, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_TRUE_SEEING); + if(oSeeMe == OBJECT_INVALID) + { + oSeeMe = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oCreature, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_SEE_INVISIBILITY); + } + if(oSeeMe == OBJECT_INVALID) + { + // Check non-invisibility options first. Since they can be used + // while near enemies. + if(GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT, oCreature)) + { + // Go into stealth mode + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + if(AI_DEBUG) ai_Debug("0i_actions", "207", "Using HIDE_IN_PLAIN_SIGHT!"); + return TRUE; + } + if(nDarkness) + { + ai_SetLastAction(oCreature, SPELL_DARKVISION); + ActionCastSpellAtObject(SPELL_DARKVISION, oCreature); + return TRUE; + } + if(GetHasSpell(SPELL_ETHEREALNESS, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_ETHEREALNESS); + ActionCastSpellAtObject(SPELL_ETHEREALNESS, oCreature); + return TRUE; + } + if(GetHasSpell(SPELL_SANCTUARY, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_SANCTUARY); + ActionCastSpellAtObject(SPELL_SANCTUARY, oCreature); + return TRUE; + } + // Get the nearest Enemy and how close they are. + // Use this to keep invisibility from being spammed in melee. + object oEnemy = ai_GetNearestEnemy(oCreature); + if(GetDistanceBetween(oCreature, oEnemy) > AI_RANGE_MELEE) + { + if(GetHasSpell(SPELL_IMPROVED_INVISIBILITY, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_IMPROVED_INVISIBILITY); + ActionCastSpellAtObject(SPELL_IMPROVED_INVISIBILITY, oCreature); + return TRUE; + } + if(GetHasSpell(SPELL_INVISIBILITY, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_INVISIBILITY); + ActionCastSpellAtObject(SPELL_INVISIBILITY, oCreature); + return TRUE; + } + if(GetHasSpell(SPELL_INVISIBILITY_SPHERE, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_INVISIBILITY_SPHERE); + ActionCastSpellAtObject(SPELL_INVISIBILITY_SPHERE, oCreature); + return TRUE; + } + if(GetHasSpell(799/*SPELLABILITY_VAMPIRE_INVISIBILITY*/, oCreature)) + { + ai_SetLastAction(oCreature, 799/*SPELLABILITY_VAMPIRE_INVISIBILITY*/); + ActionCastSpellAtObject(799/*SPELLABILITY_VAMPIRE_INVISIBILITY*/, oCreature); + return TRUE; + } + } + } + } + return FALSE; +} +int ai_SearchForHiddenCreature(object oCreature, int bMonster, object oInvisible = OBJECT_INVALID, float fRange = 1.0) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "358", GetName(oCreature) + " is searching for an invisible creature (" + + GetName(oInvisible) + ")."); + if(oInvisible == OBJECT_INVALID) + { + // Have we seen anyone go invisible? + oInvisible = GetLocalObject(oCreature, AI_IS_INVISIBLE); + if(oInvisible == OBJECT_INVALID || GetIsDead(oInvisible)) + { + oInvisible = ai_GetNearestEnemy(oCreature, 1, 7, PERCEPTION_HEARD_AND_NOT_SEEN); + if(oInvisible == OBJECT_INVALID) oInvisible = ai_GetNearestEnemy(oCreature); + } + } + float fPerceptionDistance, fDistance; + if(bMonster) + { + GetDistanceBetween(oCreature, oInvisible); + fPerceptionDistance = GetLocalFloat(GetModule(), AI_RULE_PERCEPTION_DISTANCE); + } + else + { + // 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); + fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); + if(fPerceptionDistance == 0.0) fPerceptionDistance = 20.0; + } + if(AI_DEBUG) ai_Debug("0i_actions", "383", "Is invisible: " + GetName(oInvisible) + + " fDistance: " + FloatToString(fDistance, 0, 2) + + " fPerceptionDistance: " + FloatToString(fPerceptionDistance, 0, 2)); + // Might need to end combat at this point? + if(fDistance > fPerceptionDistance) return FALSE; + // If we are close enough then lets look for them. + if(fDistance < AI_RANGE_LONG) + { + // nHidden 1 = Invisible effects, 2 = Darkness effects, 3 = Sanctuary effects, 4 Stealth. + int nHidden = ai_GetIsHidden(oInvisible); + if(nHidden) + { + // They have a magical effect! Is there a spell we can use to see? + if(nHidden < 4) + { + if(AI_DEBUG) ai_Debug("0i_actions", "399", " They are using magic to hide: " + + IntToString(nHidden)); + // True Seeing pierces all types of magical hiding. + if(GetHasSpell(SPELL_TRUE_SEEING, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_TRUE_SEEING); + ActionCastSpellAtObject(SPELL_TRUE_SEEING, oCreature); + return TRUE; + } + if(nHidden == 1 || nHidden == 3) // Invisibility or Ethereal effect. + { + if(GetHasSpell(SPELL_SEE_INVISIBILITY, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_SEE_INVISIBILITY); + ActionCastSpellAtObject(SPELL_SEE_INVISIBILITY, oCreature); + return TRUE; + } + if(GetHasSpell(SPELL_INVISIBILITY_PURGE, oCreature)) + { + ai_SetLastAction(oCreature, SPELL_INVISIBILITY_PURGE); + ActionCastSpellAtObject(SPELL_INVISIBILITY_PURGE, oCreature); + return TRUE; + } + } + if(nHidden == 2) // Darkness spell effect. + { + if(GetHasSpell(SPELL_DARKVISION)) + { + ai_SetLastAction(oCreature, SPELL_DARKVISION); + ActionCastSpellAtObject(SPELL_DARKVISION, oCreature); + return TRUE; + } + } + // To be able to attack a magically hidden foe we have to be + // with in melee attack range. Cannot hear Ethereal foes! + // We will automatically hear them once we are within range. + // We also walk so we don't give attacks of opportunity. + if(nHidden < 3) + { + if(AI_DEBUG) ai_Debug("0i_actions", "437", " We have no spells to counter with. Moving up to attack!"); + SetLocalInt(oCreature, AI_AM_I_SEARCHING, TRUE); + ActionMoveToObject(oInvisible); + ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING)); + if(ai_GetIsInCombat(oCreature)) ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + } + else // They are using stealth! + { + if(AI_DEBUG) ai_Debug("0i_actions", "447", " Using Detect mode and moving up."); + SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE); + SetLocalInt(oCreature, AI_AM_I_SEARCHING, TRUE); + // We use to move to the object but that is creepy! + //ActionMoveToObject(oInvisible, FALSE, fRange); + ActionMoveToLocation(GetLocation(oInvisible), FALSE); + ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING)); + if(ai_GetIsInCombat(oCreature)) ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + } + else // They are not hidden, then that means we can hear them but not see them. + // Probably behind a wall or door. + { + SetLocalInt(oCreature, AI_AM_I_SEARCHING, TRUE); + // We use to move to the object but that is creepy! + //ActionMoveToObject(oInvisible, FALSE, fRange); + ActionMoveToLocation(GetLocation(oInvisible), FALSE); + ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING)); + if(ai_GetIsInCombat(oCreature)) ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + } + else // We need to get closer to start looking for them. + { + if(AI_DEBUG) ai_Debug("0i_actions", "469", "Moving towards invisible creature from a distance: " + GetName(oInvisible)); + SetLocalInt(oCreature, AI_AM_I_SEARCHING, TRUE); + // We use to move to the object but that is creepy! + //ActionMoveToObject(oInvisible, TRUE, 14.0); + ActionMoveToLocation(GetLocation(oInvisible), FALSE); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING))); + if(ai_GetIsInCombat(oCreature)) ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + return FALSE; +} +int ai_MoralCheck(object oCreature) +{ + // If we are immune to fear then we are immune to MoralChecks! + // Constructs and Undead are also immune to fear. + int nRaceType = GetRacialType(oCreature); + if(!GetLocalInt(GetModule(), AI_RULE_MORAL_CHECKS) || GetIsImmune(oCreature, IMMUNITY_TYPE_FEAR) || + nRaceType == RACIAL_TYPE_UNDEAD || + nRaceType == RACIAL_TYPE_CONSTRUCT || + ai_GetIsCharacter(oCreature)) return FALSE; + // Moral DC is AI_WOUNDED_MORAL_DC - The number of allies. + // or AI_BLOODY_MORAL_DC - number of allies. + int nDC; + int nHpPercent = ai_GetPercHPLoss(oCreature); + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + // We only make moral checks if we are below half hitpoints and the Difficulty should be adjusted to -10 at 0. + 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(nHpPercent <= AI_HEALTH_BLOODY) nDC = AI_BLOODY_MORAL_DC; + else nDC = AI_WOUNDED_MORAL_DC; + nDC = nDC - GetLocalInt(oCreature, AI_ALLY_NUMBERS); + if(nDC < 1) nDC = 1; + if(AI_DEBUG) ai_Debug("0i_talents", "367", "Moral check DC: " + IntToString(nDC) + "."); + //SendMessageToPC(GetFirstPC(), "0i_talents, 431, " + GetName(oCreature) + " Moral check DC: " + IntToString(nDC) + "."); + if(!WillSave(oCreature, nDC, SAVING_THROW_TYPE_FEAR, oNearestEnemy)) + { + if(AI_DEBUG) ai_Debug("0i_talents", "370", "Moral check failed, we are fleeing!"); + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_coward"); + effect eVFX = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVFX, oCreature, 6.0f); + ActionMoveAwayFromObject(oNearestEnemy, TRUE, AI_RANGE_LONG); + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) + { + int nRoll = d4(); + if(nRoll == 1) PlayVoiceChat(VOICE_CHAT_FLEE, oCreature); + else if(nRoll == 2) PlayVoiceChat(VOICE_CHAT_GUARDME, oCreature); + else if(nRoll == 3) PlayVoiceChat(VOICE_CHAT_HELP, oCreature); + else if(nRoll == 4 && nHpPercent < 100) PlayVoiceChat(VOICE_CHAT_HEALME, oCreature); + } + return TRUE; + } + if(nDC >= 11 && !ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) + { + int nRoll = d6(); + // Cry out when you are overwhelmed! + if(nRoll == 1) PlayVoiceChat(VOICE_CHAT_CUSS, oCreature); + else if(nRoll == 2) PlayVoiceChat(VOICE_CHAT_BADIDEA, oCreature); + else if(nRoll == 3) PlayVoiceChat(VOICE_CHAT_ENEMIES, oCreature); + } + } + return FALSE; +} +int ai_GetInAOEReaction(object oCreature, object oCaster, int nSpell) +{ + switch(nSpell) + { + case SPELL_ACID_FOG: + case SPELL_CLOUDKILL: + case SPELL_CREEPING_DOOM: + { + // Nothing but bad times with these spells. + return TRUE; + } + case SPELL_STORM_OF_VENGEANCE: + { + // This only harms our enemies! + return (oCaster != oCreature && GetIsEnemy(oCaster, oCreature)); + } + // They should only flee Silence if they want to cast a spell! + //case SPELL_SILENCE: + case SPELL_BLADE_BARRIER: + case SPELL_WALL_OF_FIRE: + case SPELL_INCENDIARY_CLOUD: + { + // Check reflex feats and saves. + return (!GetHasFeat(FEAT_EVASION, oCreature) && + !GetHasFeat(FEAT_IMPROVED_EVASION, oCreature) && + GetReflexSavingThrow(oCreature) < 21 + d6()); + } + case SPELL_STINKING_CLOUD: + { + // Do we have a high fortitude save? 20 + 5 + return (GetFortitudeSavingThrow(oCreature) < 20 + d6()); + } + case SPELL_GREASE: + case SPELL_ENTANGLE: + case SPELL_VINE_MINE_ENTANGLE: + case SPELL_WEB: + { + // Do we have a high reflex save? d20 + 1 + return (!GetHasFeat(FEAT_WOODLAND_STRIDE, oCreature) && + !GetLocalInt(oCreature, "X2_L_IS_INCORPOREAL") && + GetReflexSavingThrow(oCreature) < 15 + d6()); + } + case SPELL_EVARDS_BLACK_TENTACLES: + { + // Small creatures are immune and can they hit me? d20 + 8 + caster lvl(7) + return (GetCreatureSize(oCreature) > 2 && + GetAC(oCreature) < 30 + d6()); + } + case SPELL_CLOUD_OF_BEWILDERMENT: + { + // Do we have a high fortitude save? 20 + 2 + return (GetFortitudeSavingThrow(oCreature) < 17 + d6()); + } + case SPELL_MIND_FOG: + case SPELL_STONEHOLD: + { + // Do we have a high enough will save? 20 + 6 + return (GetWillSavingThrow(oCreature) < 21 + d6()); + } + case SPELL_SPIKE_GROWTH: + case SPELL_VINE_MINE_HAMPER_MOVEMENT: + { + // Do we have a high reflex save? d20 + 3 + return (GetReflexSavingThrow(oCreature) < 18 + d6()); + } + } + return FALSE; +} +void ai_HaveCreatureSpeak(object oCreature, int nRoll, string sVoiceChatArray, int bImportant = FALSE) +{ + if(ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK) && !bImportant) return; + if(nRoll == 0) + { + // Some races shouldn't talk. + int nRacialType = GetRacialType(oCreature); + if(nRacialType == RACIAL_TYPE_ANIMAL || nRacialType == RACIAL_TYPE_BEAST || + nRacialType == RACIAL_TYPE_MAGICAL_BEAST || nRacialType == RACIAL_TYPE_OOZE || + nRacialType == RACIAL_TYPE_UNDEAD || nRacialType == RACIAL_TYPE_VERMIN) return; + SpeakString(sVoiceChatArray); + return; + } + nRoll = Random(nRoll); + string sVoice = ai_GetStringArray(sVoiceChatArray, nRoll); + if(sVoice != "") PlayVoiceChat(StringToInt(sVoice), oCreature); +} +int ai_CheckForAssociateSpellTalent(object oAssociate, int nInMelee, int nMaxLevel, int nRound = 0) +{ + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + if(!ai_GetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING)) + { + if(ai_UseCreatureTalent(oAssociate, AI_TALENT_INDISCRIMINANT_AOE, nInMelee, nMaxLevel)) return TRUE; + if(ai_UseCreatureTalent(oAssociate, AI_TALENT_DISCRIMINANT_AOE, nInMelee, nMaxLevel)) return TRUE; + } + if(ai_GetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING)) return FALSE; + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oAssociate); + return ai_TryDefensiveTalents(oAssociate, nInMelee, nMaxLevel, nRound, oTarget); +} +void ai_DoPhysicalAttackOnBest(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) +{ + talent tUse; + object oTarget; + if(AI_DEBUG) ai_Debug("0i_actions", "496", "Check for ranged attack on nearest enemy!"); + // ************************** Ranged feat attacks ************************** + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + !ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && + ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + // Lets pick off the nearest targets first. + if(!nInMelee) + { + 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); + } + 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) + { + if(ai_TryRapidShotFeat(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; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + if(AI_DEBUG) ai_Debug("0i_actions", "525", "Check for melee attack on nearest enemy!"); + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee, bAlwaysAtk)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, bAlwaysAtk); + // If we don't find a target then we don't want to fight anyone! + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + if(AI_DEBUG) ai_Debug("0i_actions", "536", "Do melee attack against nearest: " + GetName(oTarget) + "!"); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} +void ai_DoPhysicalAttackOnNearest(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) +{ + talent tUse; + object oTarget; + if(AI_DEBUG) ai_Debug("0i_actions", "496", "Check for ranged attack on nearest enemy!"); + // ************************** Ranged feat attacks ************************** + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + !ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && + ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + // Lets pick off the nearest targets first. + if(!nInMelee) + { + 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); + } + 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) + { + if(ai_TryRapidShotFeat(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; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + if(AI_DEBUG) ai_Debug("0i_actions", "525", "Check for melee attack on nearest enemy!"); + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee, bAlwaysAtk)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee, bAlwaysAtk); + // If we don't find a target then we don't want to fight anyone! + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + if(AI_DEBUG) ai_Debug("0i_actions", "536", "Do melee attack against nearest: " + GetName(oTarget) + "!"); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} +void ai_DoPhysicalAttackOnLowestCR(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "533", "Check for ranged attack on weakest enemy!"); + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + !ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && + ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + // Lets pick off the weaker targets. + if(!nInMelee) + { + 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); + } + 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) + { + if(ai_TryRapidShotFeat(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; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + if(AI_DEBUG) ai_Debug("0i_actions", "571", "Check for melee attack on weakest enemy!"); + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee, bAlwaysAtk)) 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_RANGE_PERCEPTION, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTargetForMeleeCombat(oCreature, nInMelee, bAlwaysAtk); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + if(AI_DEBUG) ai_Debug("0i_actions", "577", GetName(OBJECT_SELF) + " does melee attack against weakest: " + GetName(oTarget) + "!"); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} +int ai_InCombatEquipBestMeleeWeapon(object oCreature) +{ + if(ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature))) return FALSE; + if(ai_EquipBestMeleeWeapon(oCreature)) + { + // We delay 1 second since ActionEquip is not an action we can check for. + // This keeps event scripts from clearing before we actually equip. + SetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS, 2); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + return FALSE; +} +int ai_InCombatEquipBestRangedWeapon(object oCreature) +{ + if(ai_EquipBestRangedWeapon(oCreature)) + { + // We delay 1 second since ActionEquip is not an action we can check for. + // This keeps event scripts from clearing before we actually equip. + SetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS, 1); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + return FALSE; +} +int ai_CheckItemForHealing(object oCreature, object oTarget, object oItem, int nHpLost, int bEquiped = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "629", "Checking Item properties on " + GetName(oItem)); + int nIprpSubType, nSpell, nLevel, nIPType; + itemproperty ipProp = GetFirstItemProperty(oItem); + // Lets skip this if there are no properties. + if(!GetIsItemPropertyValid(ipProp)) return FALSE; + // Check for cast spell property and add them to the talent list. + int nIndex; + ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + if(AI_DEBUG) ai_Debug("0i_actions", "639", "ItempropertyType(15): " + IntToString(GetItemPropertyType(ipProp))); + nIPType = GetItemPropertyType(ipProp); + if(nIPType == ITEM_PROPERTY_CAST_SPELL) + { + nIprpSubType = GetItemPropertySubType(ipProp); + nSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nIprpSubType)); + if(ai_ShouldWeCastThisCureSpell(nSpell, nHpLost)) + { + // We have established that we can use the item if it is equiped. + if(!bEquiped) ai_CheckIfCanUseItem(oCreature, oItem); + // Get how they use the item (charges or uses per day). + int nUses = GetItemPropertyCostTableValue(ipProp); + if(nUses > 1 && nUses < 7) + { + int nCharges = GetItemCharges(oItem); + if(AI_DEBUG) ai_Debug("0i_actions", "654", "Item charges: " + IntToString(nCharges)); + if(nUses == 6 && nCharges < 1 || nUses == 5 && nCharges < 3 || + nUses == 4 && nCharges < 5 || nUses == 3 && nCharges < 7 || + nUses == 2 && nCharges < 9) return FALSE; + } + else if(nUses > 7 && nUses < 13) + { + int nPerDay = GetItemPropertyUsesPerDayRemaining(oItem, ipProp); + if(AI_DEBUG) ai_Debug("0i_actions", "662", "Item uses: " + IntToString(nPerDay)); + if(nPerDay == 0) return FALSE; + } + // SubType is the ip spell index for iprp_spells.2da + nIprpSubType = GetItemPropertySubType(ipProp); + if(AI_DEBUG) ai_Debug("0i_actions", "667", GetName(oCreature) + " is using " + GetName(oItem) + " on " + GetName(oTarget) + "."); + ActionUseItemOnObject(oItem, ipProp, oTarget, nIprpSubType); + return TRUE; + } + } + nIndex++; + ipProp = GetNextItemProperty(oItem); + } + return FALSE; +} +int ai_HealSickness(object oCreature, object oTarget, object oPC, int nSickness, int bForce = FALSE) +{ + // If the player is not forcing a check. + if(!bForce) + { + // Is Casting Cure spells off? + if(ai_GetMagicMode(oCreature, AI_MAGIC_CURE_SPELLS_OFF)) return FALSE; + // Do we have no magic on? + if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) return FALSE; + // Should we ignore associates? + if(ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && + GetAssociateType(oTarget) > 1) return FALSE; + } + // Check for spells. + if(nSickness == AI_ALLY_IS_DISEASED) + { + if(AI_DEBUG) ai_Debug("0i_actions", "717", "Attempting to remove disease."); + if(ai_CheckAndCastSpell(oCreature, SPELL_REMOVE_DISEASE, 0, 0.0, oTarget)) return TRUE; + } + else if(nSickness == AI_ALLY_IS_POISONED) + { + if(AI_DEBUG) ai_Debug("0i_actions", "726", "Attempting to remove poison."); + if(ai_CheckAndCastSpell(oCreature, SPELL_NEUTRALIZE_POISON, 0, 0.0, oTarget)) return TRUE; + } + else if(nSickness == AI_ALLY_IS_WEAK) + { + if(AI_DEBUG) ai_Debug("0i_actions", "735", "Attempting to remove ability score drain."); + if(ai_CheckAndCastSpell(oCreature, SPELL_LESSER_RESTORATION, 0, 0.0, oTarget)) return TRUE; + if(ai_CheckAndCastSpell(oCreature, SPELL_RESTORATION, 0, 0.0, oTarget)) return TRUE; + if(ai_CheckAndCastSpell(oCreature, SPELL_GREATER_RESTORATION, 0, 0.0, oTarget)) return TRUE; + } + else return FALSE; + // Check for healing kits. + if(!GetLocalInt(GetModule(), AI_RULE_HEALERSKITS)) return FALSE; + int nIprpSubType, nSpell; + itemproperty ipProp; + object oItem = GetFirstItemInInventory(oCreature); + while(oItem != OBJECT_INVALID) + { + if(GetIdentified(oItem)) + { + int nBaseItemType = GetBaseItemType(oItem); + if(nBaseItemType == BASE_ITEM_HEALERSKIT && + (nSickness == AI_ALLY_IS_DISEASED || + nSickness == AI_ALLY_IS_POISONED)) + { + ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + if(GetItemPropertyType(ipProp) == ITEM_PROPERTY_HEALERS_KIT) + { + if(AI_DEBUG) ai_Debug("0i_actions", "772", "Attempting to remove (" + IntToString(nSickness) + ") with a healing kit."); + if(ai_GetIsCharacter(oPC)) ai_SendMessages(GetName(oCreature) + " uses " + GetName(oItem) + " on " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + ipProp = GetNextItemProperty(oItem); + } + } + else if(nBaseItemType == BASE_ITEM_POTIONS || + nBaseItemType == BASE_ITEM_ENCHANTED_POTION || + nBaseItemType == FEAT_BREW_POTION) + { + ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + nIprpSubType = GetItemPropertySubType(ipProp); + nSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nIprpSubType)); + if(AI_DEBUG) ai_Debug("0i_actions", "789", "Checking potion, " + IntToString(nSpell)); + if(nSpell == SPELL_REMOVE_DISEASE && nSickness == AI_ALLY_IS_DISEASED) + { + if(AI_DEBUG) ai_Debug("0i_actions", "786", "Using a potion of Remove Disease."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + if(nSpell == SPELL_NEUTRALIZE_POISON && nSickness == AI_ALLY_IS_POISONED) + { + if(AI_DEBUG) ai_Debug("0i_actions", "786", "Using a potion of Neturalize Poison."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + if(nSpell == SPELL_LESSER_RESTORATION && nSickness == AI_ALLY_IS_WEAK) + { + if(AI_DEBUG) ai_Debug("0i_actions", "781", "Using a potion of Lesser Restoration."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + if(nSpell == SPELL_RESTORATION && nSickness == AI_ALLY_IS_WEAK) + { + if(AI_DEBUG) ai_Debug("0i_actions", "791", "Using a potion of Restoration."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + ipProp = GetNextItemProperty(oItem); + } + } + else if(nBaseItemType == BASE_ITEM_SCROLL || + nBaseItemType == BASE_ITEM_ENCHANTED_SCROLL || + nBaseItemType == BASE_ITEM_SPELLSCROLL || + nBaseItemType == BASE_ITEM_ENCHANTED_WAND || + nBaseItemType == BASE_ITEM_MAGICWAND || + nBaseItemType == BASE_ITEM_MAGICSTAFF) + { + if(ai_CheckIfCanUseItem(oCreature, oItem)) + { + ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + nSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nIprpSubType)); + if(nSpell == SPELL_REMOVE_DISEASE && nSickness == AI_ALLY_IS_DISEASED) + { + if(AI_DEBUG) ai_Debug("0i_actions", "786", "Using a potion of Remove Disease."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + if(nSpell == SPELL_NEUTRALIZE_POISON && nSickness == AI_ALLY_IS_POISONED) + { + if(AI_DEBUG) ai_Debug("0i_actions", "786", "Using a potion of Neturalize Poison."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + if(nSpell == SPELL_LESSER_RESTORATION && nSickness == AI_ALLY_IS_WEAK) + { + if(AI_DEBUG) ai_Debug("0i_actions", "781", "Using a potion of Lesser Restoration."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + if(nSpell == SPELL_RESTORATION && nSickness == AI_ALLY_IS_WEAK) + { + if(AI_DEBUG) ai_Debug("0i_actions", "791", "Using a potion of Restoration."); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + ipProp = GetNextItemProperty(oItem); + } + } + } + } + oItem = GetNextItemInInventory(oCreature); + } + return FALSE; +} +int ai_UseHealingItem(object oCreature, object oTarget, object oPC) +{ + if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC_ITEMS)) return FALSE; + string sSlots; + int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); + itemproperty ipProp; + // Cycle through all the creatures equiped items. + int nSlot; + object oItem = GetItemInSlot(nSlot, oCreature); + while(nSlot < 11) + { + if(oItem != OBJECT_INVALID && + ai_CheckItemForHealing(oCreature, oTarget, oItem, nDamage, TRUE)) return TRUE; + oItem = GetItemInSlot(++nSlot, oCreature); + } + 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_actions", "696", GetName(oItem) + " requires " + Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)) + " slots."); + if(sSlots == "0x00000") + { + int nBaseItemType = GetBaseItemType(oItem); + // Lets not use up our healing kits on minor damage. + if(nBaseItemType == BASE_ITEM_HEALERSKIT) + { + if(!GetLocalInt(GetModule(), AI_RULE_HEALERSKITS)) return FALSE; + ipProp = GetFirstItemProperty(oItem); + if(GetItemPropertyType(ipProp) == ITEM_PROPERTY_HEALERS_KIT) + { + if(ai_GetIsCharacter(oPC)) ai_SendMessages(GetName(oCreature) + " uses " + GetName(oItem) + " on " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); + ActionUseItemOnObject(oItem, ipProp, oTarget); + return TRUE; + } + } + // Do we want Player AI and Associates to use potions on others? + //else if(nBaseItemType == BASE_ITEM_ENCHANTED_POTION || + // nBaseItemType == BASE_ITEM_POTIONS) + //{ + // if(oCaster == oTarget) + // { + // if(ai_CheckItemForHealing(oCreature, oTarget, oItem, nDamage)) return TRUE; + // } + //} + else if(ai_CheckItemForHealing(oCreature, oTarget, oItem, nDamage)) return TRUE; + } + } + oItem = GetNextItemInInventory(oCreature); + } + return FALSE; +} +void ai_ActionTryHealing(object oCreature, object oTarget) +{ + ai_TryHealing(oCreature, oTarget, TRUE); +} +int ai_TryHealing(object oCreature, object oTarget, int bForce = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "733", "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")) + + " Undead: " + IntToString(GetRacialType(oTarget) == RACIAL_TYPE_UNDEAD)); + // If the player is not forcing a check. + if(!bForce) + { + // Should we ignore associates? + if(ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && + GetAssociateType(oTarget) > 1) return FALSE; + } + // Limits the number of times a wounded creature will ask for help. + if(GetLocalInt(oCreature, "AI_WOUNDED_SHOUT_LIMIT") > 3) return FALSE; + // This keeps everyone from healing the same character in one round and over healing! + if(oCreature == oTarget) DeleteLocalInt(oTarget, "AI_I_AM_BEING_HEALED"); + else if(GetLocalInt(oTarget, "AI_I_AM_BEING_HEALED")) return FALSE; + if(ai_GetAIMode(oCreature, AI_MODE_PARTY_HEALING_OFF) && + 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(bForce && nHpLost < 100) nHpLost = 0; + if(AI_DEBUG) ai_Debug("0i_actions", "743", "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)) + { + 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", "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); + } + } + else if(nEffectType == EFFECT_TYPE_POISON) + { + 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); + } + } + else if(nEffectType == EFFECT_TYPE_ABILITY_DECREASE) + { + 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); + } + } + eEffect = GetNextEffect(oTarget); + } + return FALSE; + } + // Do they have Lay on Hands? + if(GetHasFeat(FEAT_LAY_ON_HANDS, oCreature)) + { + int nCanHeal = GetAbilityModifier(ABILITY_CHARISMA, oCreature) * ai_GetCharacterLevels(oCreature); + if(nCanHeal <= nHpLost) + { + ai_UseFeat(oCreature, FEAT_LAY_ON_HANDS, oTarget); + return TRUE; + } + } + object oMaster = ai_GetPlayerMaster(oCreature); + // 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_CastMemorizedHealing(oCreature, oTarget, oMaster, nClass)) + { + SetLocalInt(oTarget, "AI_I_AM_BEING_HEALED", TRUE); + return TRUE; + } + } + else if(ai_CastKnownHealing(oCreature, oTarget, oMaster, nClass)) + { + SetLocalInt(oTarget, "AI_I_AM_BEING_HEALED", TRUE); + return TRUE; + } + } + nPosition++; + } + } + // We have exhausted all attempts to use normal healing spells. + if(ai_UseHealingItem(oCreature, oTarget, oMaster)) + { + SetLocalInt(oTarget, "AI_I_AM_BEING_HEALED", TRUE); + return TRUE; + } + // Final attempt to heal oTarget, check for Spontaneous cure spells. + if(ai_CastSpontaneousCure(oCreature, oTarget, oMaster)) + { + SetLocalInt(oTarget, "AI_I_AM_BEING_HEALED", TRUE); + return TRUE; + } + // 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; +} +int ai_PerceiveEnemy(object oCreature, object oEnemy) +{ + float fDistance = GetDistanceBetween(oCreature, oEnemy); + if(fDistance < 50.0) + { + // Game is in meters, so 1 foot = 3.333 meter + // penalty is -1 per 10' so divide it by 10 to use 0.3333f + int nDC = 10 + FloatToInt(fDistance * 0.3333f); + // Check to see if the creature is hiding and add the creatures checks. + int nEnemyMoveSilent, nEnemyHide; + if(GetStealthMode(oEnemy)) + { + nEnemyMoveSilent =(d20() + GetSkillRank(SKILL_MOVE_SILENTLY, oEnemy)); + nEnemyHide =(d20() + GetSkillRank(SKILL_HIDE, oEnemy)); + } + if(GetIsSkillSuccessful (oCreature, SKILL_SPOT, nDC + nEnemyHide)) return TRUE; + if(GetIsSkillSuccessful (oCreature, SKILL_LISTEN, nDC + nEnemyMoveSilent)) return TRUE; + } + return FALSE; +} +void ai_ScoutAhead(object oCreature) +{ + object oPerceived; + object oEnemy = ai_GetNearestEnemy(oCreature, 1, -1, -1, -1, -1, TRUE); + // We see them so fight! + if(oEnemy != OBJECT_INVALID) + { + if(ai_PerceiveEnemy(oCreature, oEnemy)) + { + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) + { + int nRoll = d10(); + if(nRoll == 1) PlayVoiceChat(VOICE_CHAT_ENEMIES, oCreature); + else if(nRoll == 2) PlayVoiceChat(VOICE_CHAT_FOLLOWME, oCreature); + else if(nRoll == 3) PlayVoiceChat(VOICE_CHAT_LOOKHERE, oCreature); + } + ActionMoveToObject(oEnemy, TRUE, AI_RANGE_LONG); + return; + } + // There are enemies here so lets go to them. + else + { + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) + { + int nRoll = d3(); + if(nRoll == 1) PlayVoiceChat(VOICE_CHAT_BADIDEA, oCreature); + else if(nRoll == 2) PlayVoiceChat(VOICE_CHAT_SEARCH, oCreature); + else if(nRoll == 3) PlayVoiceChat(VOICE_CHAT_FOLLOWME, oCreature); + } + ActionMoveToObject(oEnemy, TRUE, AI_RANGE_CLOSE); + } + } + // There are no more enemies, but we must look like we are patroling so + // go to encounter points. + else + { + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) + { + int nRoll = d10(); + if(nRoll == 1) PlayVoiceChat(VOICE_CHAT_BADIDEA, oCreature); + else if(nRoll == 2) PlayVoiceChat(VOICE_CHAT_SEARCH, oCreature); + else if(nRoll == 3) PlayVoiceChat(VOICE_CHAT_FOLLOWME, oCreature); + } + // No enemy so lets get a spawn point! + object oSpawnPoint = GetNearestObjectByTag("ip_encounter", oCreature, d6()); + ActionMoveToObject(oSpawnPoint, TRUE, AI_RANGE_CLOSE); + } +} +int ai_ShouldIPickItUp(object oCreature, object oItem) +{ + int nMinGold; + if(GetResRef(oItem) == "nw_it_gold001") return TRUE; + int nBaseItem = GetBaseItemType(oItem); + if(GetPlotFlag(oItem)) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_PLOT)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_2"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_ARMOR) + { + if (ai_GetLootFilter(oCreature, AI_LOOT_ARMOR)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_3"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_BELT) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_BELTS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_4"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_BOOTS) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_BOOTS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_5"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_CLOAK) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_CLOAKS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_6"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_GEM) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_GEMS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_7"); + else return FALSE; + } + else if((nBaseItem == BASE_ITEM_BRACER|| nBaseItem == BASE_ITEM_GLOVES)) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_GLOVES)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_8"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_HELMET) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_HEADGEAR)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_9"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_AMULET || nBaseItem == BASE_ITEM_RING) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_JEWELRY)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_10"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_BLANK_POTION || nBaseItem == BASE_ITEM_POTIONS || + nBaseItem == BASE_ITEM_ENCHANTED_POTION) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_POTIONS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_12"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_BLANK_SCROLL || nBaseItem == BASE_ITEM_SCROLL || + nBaseItem == BASE_ITEM_ENCHANTED_SCROLL || nBaseItem == BASE_ITEM_SPELLSCROLL) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_SCROLLS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_13"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_BLANK_WAND || nBaseItem == BASE_ITEM_ENCHANTED_WAND || + nBaseItem == BASE_ITEM_MAGICWAND || nBaseItem == BASE_ITEM_MAGICROD || + nBaseItem == BASE_ITEM_MAGICSTAFF) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_WANDS_RODS_STAVES)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_15"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_ARROW) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_ARROWS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_17"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_BOLT) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_BOLTS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_18"); + else return FALSE; + } + else if(nBaseItem == BASE_ITEM_BULLET) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_BULLETS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_19"); + else return FALSE; + } + else if(ai_GetIsWeapon(oItem)) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_WEAPONS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_16"); + else return FALSE; + } + else if(ai_GetIsShield(oItem)) + { + if(ai_GetLootFilter(oCreature, AI_LOOT_SHIELDS)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_14"); + else return FALSE; + } + else if(ai_GetLootFilter(oCreature, AI_LOOT_MISC)) nMinGold = GetLocalInt(oCreature, "AI_MIN_GOLD_11"); + else return FALSE; + // Check if it is too heavy. + int nItemWeight = GetWeight(oItem); + if(AI_DEBUG) ai_Debug("0i_actions", "1146", GetName(oItem) + " nItemWeight: " + + IntToString(nItemWeight) + " Max Weight: " + IntToString(GetLocalInt(oCreature, AI_MAX_LOOT_WEIGHT) * 10)); + if(nItemWeight > GetLocalInt(oCreature, AI_MAX_LOOT_WEIGHT) * 10) return FALSE; + // Check if it is not valuable enough. + int bID = GetIdentified(oItem); + if(!bID) SetIdentified(oItem, TRUE); + int nItemValue = GetGoldPieceValue(oItem); + if(!bID) SetIdentified(oItem, FALSE); + if(AI_DEBUG) ai_Debug("0i_actions", "998", GetName(oItem) + " nMinGold: " + IntToString(nMinGold) + " nItemValue: " + + IntToString(nItemValue) + " bID: " + IntToString(bID)); + if(nMinGold > nItemValue) return FALSE; + return TRUE; +} +void ai_TakeItemMessage(object oCreature, object oObject, object oItem, object oMaster) +{ + int bId = GetIdentified(oItem); + int nCreatureSkill = GetSkillRank(SKILL_LORE, oCreature); + int nMasterSkill = GetSkillRank(SKILL_LORE, oMaster); + if(nCreatureSkill + nMasterSkill > 0) + { + if(nCreatureSkill > nMasterSkill) ai_IdentifyItemVsKnowledge(oCreature, oItem); + else ai_IdentifyItemVsKnowledge(oMaster, oItem); + } + if(!ai_GetIsCharacter(oCreature)) + { + if(GetIdentified(oItem)) + { + if(bId) ai_SendMessages(GetName(oCreature) + " has found a " + GetName(oItem) + " from the " + GetName(oObject) + ".", AI_COLOR_GRAY, oMaster); + else ai_SendMessages(GetName(oCreature) + " has found and identified " + GetName(oItem) + " from the " + GetName(oObject) + ".", AI_COLOR_GREEN, oMaster); + } + else if(!ai_GetIsCharacter(oCreature)) + { + string sBaseName = GetStringByStrRef(StringToInt(Get2DAString("baseitems", "name", GetBaseItemType(oItem)))); + ai_SendMessages(GetName(oCreature) + " has found a " + sBaseName + " from the " + GetName(oObject) + ".", AI_COLOR_GRAY, oMaster); + } + } + else if(GetIdentified(oItem) && !bId) + { + ai_SendMessages(GetName(oCreature) + " has identified " + GetName(oItem) + " from the " + GetName(oObject) + ".", AI_COLOR_GREEN, oMaster); + } + if(GetPlotFlag(oItem)) + { + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) PlayVoiceChat(VOICE_CHAT_LOOKHERE, oCreature); + } +} +void ai_SearchObject(object oCreature, object oObject, object oMaster, int bOnce = FALSE) +{ + ai_Debug("0i_actions", "966", GetName(OBJECT_SELF) + " is opening " + GetName(oObject)); + string sTag = GetTag(oCreature); + AssignCommand(oObject, ActionPlayAnimation(ANIMATION_PLACEABLE_OPEN)); + if(GetIsTrapped(oObject)) DoPlaceableObjectAction(oObject, PLACEABLE_ACTION_USE); + SetLocalInt(oObject, "AI_LOOTED_" + sTag, TRUE); + // Big Hack to allow NPC's to loot! + string sLootScript = GetEventScript(oObject, EVENT_SCRIPT_PLACEABLE_ON_OPEN); + //ai_Debug("0i_actions", "972", "Loot script: " + sLootScript); + if(sLootScript != "") + { + // Used in Original Campaign, and SOU for loot scripts to get treasure to work. + SetLocalObject(oObject, "AI_GET_LAST_OPENED_BY", oMaster); + ExecuteScript(sLootScript, oObject); + } + AssignCommand(oObject, ActionWait(2.0f)); + AssignCommand(oObject, ActionPlayAnimation(ANIMATION_PLACEABLE_CLOSE)); + int nItemType, nGold; + object oItem = GetFirstItemInInventory(oObject); + //ai_Debug("0i_actions", "983", "Found: " + GetName(oItem) + " ResRef: " + GetResRef(oItem) + + // " in " + GetName(oObject)); + while(oItem != OBJECT_INVALID) + { + ai_Debug("0i_actions", "987", "Found: " + GetName(oItem) + " ResRef: " + GetResRef(oItem)); + if(ai_ShouldIPickItUp(oCreature, oItem)) + { + ai_Debug("0i_actions", "1002", "Taking: " + GetName(oItem)); + if(GetResRef(oItem) == "nw_it_gold001") + { + if(!ai_GetIsCharacter(oCreature)) + { + int nGold = GetItemStackSize(oItem); + DestroyObject(oItem); + ActionDoCommand(GiveGoldToCreature(oMaster, nGold)); + ActionDoCommand(ai_SendMessages(GetName(oCreature) + " has retrieved " + IntToString(nGold) + + " gold from the " + GetName(oObject) + ".", AI_COLOR_GRAY, oMaster)); + } + else AssignCommand(oCreature, ActionTakeItem(oItem, oObject)); + } + // Check if they are a henchman, companions and familiars give all items to the pc. + else if(!ai_GetLootFilter(oCreature, AI_LOOT_GIVE_TO_PC) && + GetAssociateType(oCreature) == ASSOCIATE_TYPE_HENCHMAN && + !GetPlotFlag(oItem)) + { + if(GetBaseItemFitsInInventory(GetBaseItemType(oItem), oCreature)) + { + ActionDoCommand(ai_TakeItemMessage(oCreature, oObject, oItem, oMaster)); + ActionTakeItem(oItem, oObject); + } + else + { + if(GetIdentified(oItem)) SpeakString("My inventory is full! I cannot pick up the " + GetName(oItem) + "."); + else + { + string sBaseName = GetStringByStrRef(StringToInt(Get2DAString("baseitems", "name", GetBaseItemType(oItem)))); + SpeakString("My inventory is full! I cannot pick up the " + sBaseName + "."); + } + } + } + else + { + if(GetBaseItemFitsInInventory(GetBaseItemType(oItem), oMaster)) + { + //ai_Debug("0i_actions", "1010", "Giving to master: " + GetName(oItem)); + ActionDoCommand(ai_TakeItemMessage(oCreature, oObject, oItem, oMaster)); + AssignCommand(oObject, ActionGiveItem(oItem, oMaster)); + } + else + { + if(GetIdentified(oItem)) SpeakString("Your inventory is full! You cannot take the " + GetName(oItem) + "."); + else + { + string sBaseName = GetStringByStrRef(StringToInt(Get2DAString("baseitems", "name", GetBaseItemType(oItem)))); + SpeakString("Your inventory is full! You cannot take the " + sBaseName + "."); + } + } + } + } + oItem = GetNextItemInInventory(oObject); + //ai_Debug("0i_actions", "1016", GetName(oItem) + " is the next item."); + } + //ai_Debug("0i_actions", "1018", "Setting object as looted. Check for a new Placeable."); + if(!bOnce) ActionDoCommand(ai_ActionCheckNearbyObjects(oCreature)); +} +int ai_IsContainerLootable(object oCreature, object oObject) +{ + string sTag = GetTag(oCreature); + //ai_Debug("0i_actions", "1303", GetName(oObject) + " (sTag " + GetTag(oObject) + ") " + + // "has inventory: " + IntToString(GetHasInventory(oObject)) + " Has been looted: " + + // IntToString(GetLocalInt(oObject, "AI_LOOTED_" + sTag)) + " Is Useable? " + + // IntToString(GetUseableFlag(oObject))); + if(!GetHasInventory(oObject) || !GetUseableFlag(oObject)) return FALSE; + // This associate has already looted this object, skip. + if(GetLocalInt(oObject, "AI_LOOTED_" + sTag) || ai_GetIsCharacter(oObject)) return FALSE; + return TRUE; +} +int ai_AttempToCastKnockSpell(object oCreature, object oLocked) +{ + if(GetHasSpell(SPELL_KNOCK, oCreature) && + (GetIsDoorActionPossible(oLocked, DOOR_ACTION_KNOCK) || + GetIsPlaceableObjectActionPossible(oLocked, PLACEABLE_ACTION_KNOCK)) && + ai_GetIsInLineOfSight(oCreature, oLocked)) + { + SetLocalInt(oLocked, AI_OBJECT_IN_USE, TRUE); + DelayCommand(6.0, DeleteLocalInt(oLocked, AI_OBJECT_IN_USE)); + AssignCommand(oCreature, ai_ClearCreatureActions()); + AssignCommand(oCreature, ActionWait(1.0)); + AssignCommand(oCreature, ActionCastSpellAtObject(SPELL_KNOCK, oLocked)); + AssignCommand(oCreature, ActionWait(1.0)); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oLocked, AI_OBJECT_IN_USE))); + return TRUE; + } + return FALSE; +} +int ai_ReactToTrap(object oCreature, object oTrap, int bForce = FALSE) +{ + int nTrapDC = GetTrapDisarmDC(oTrap); + if(AI_DEBUG) ai_Debug("0i_actions", "1520", "Reacting to trap on " + GetName(oTrap) + + " bForce: " + IntToString(bForce) + " nTrapDC: " + IntToString(nTrapDC) + + " [AI_OBJECT_IN_USE: " + IntToString(GetLocalInt(oTrap, AI_OBJECT_IN_USE)) + "]."); + if(nTrapDC == 0) return FALSE; + string sTag = GetTag(oCreature); + if(bForce || ai_GetAIMode(oCreature, AI_MODE_DISARM_TRAPS)) + { + if(GetTrapDisarmable(oTrap)) + { + if(GetLocalInt(oTrap, AI_OBJECT_IN_USE)) return FALSE; + // We must have ranks in disable traps to actually disable the trap! + if(GetSkillRank(SKILL_DISABLE_TRAP, oCreature, TRUE)) + { + int nSkill = GetSkillRank(SKILL_DISABLE_TRAP, oCreature); + if(AI_DEBUG) ai_Debug("0i_actions", "1534", "nSkill: " + IntToString(nSkill) + + " + 20 = " + IntToString(nSkill + 20) + " nTrapDC: " + IntToString(nTrapDC)); + if(nSkill + 20 >= nTrapDC) + { + SetLocalInt(oTrap, AI_OBJECT_IN_USE, TRUE); + DelayCommand(18.0, DeleteLocalInt(oTrap, AI_OBJECT_IN_USE)); + AssignCommand(oCreature, ai_ClearCreatureActions()); + AssignCommand(oCreature, ActionUseSkill(SKILL_DISABLE_TRAP, oTrap, 0)); + // Let them know we did it! + AssignCommand(oCreature, ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 6, ":44:42:31:35:"))); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oTrap, AI_OBJECT_IN_USE))); + // Continue checking for traps, locks, and loot. + AssignCommand(oCreature, ActionDoCommand(ai_ActionCheckNearbyObjects(oCreature))); + return TRUE; + } + if(GetHasSpell(SPELL_FIND_TRAPS, oCreature)) + { + AssignCommand(oCreature, ai_ClearCreatureActions()); + AssignCommand(oCreature, ActionCastSpellAtObject(SPELL_FIND_TRAPS, oTrap)); + // Continue checking for traps, locks, and loot. + AssignCommand(oCreature, ActionDoCommand(ai_ActionCheckNearbyObjects(oCreature))); + return TRUE; + } + } + if(GetLocalInt(oTrap, "AI_CANNOT_TRAP_" + sTag) && !bForce) return FALSE; + // Let them know we can't get this done!. + //StrRef(40551) "I cannot disarm this trap!" + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, GetStringByStrRef(40551))); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oTrap, "AI_CANNOT_TRAP_" + sTag, TRUE); + return FALSE; + } + if(GetLocalInt(oTrap, "AI_SAW_TRAP_" + sTag) && !bForce) return FALSE; + // Let them know we can't get this done!. + ai_HaveCreatureSpeak(oCreature, 0, "I'm not skilled enough to disable the trap!", TRUE); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oTrap, "AI_SAW_TRAP_" + sTag, TRUE); + return FALSE; + } + if(GetObjectType(oTrap) == OBJECT_TYPE_TRIGGER) + { + object oMaster = ai_GetPlayerMaster(oCreature); + if(oMaster != OBJECT_INVALID && !ai_GetIsCharacter(oCreature) && + !ai_GetAIMode(oCreature, AI_MODE_IGNORE_TRAPS)) + { + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, TRUE); + ai_SetAIMode(oCreature, AI_MODE_FOLLOW, FALSE); + ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE); + int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI); + ai_HighlightWidgetMode(oMaster, oCreature, nToken); + aiSaveAssociateModesToDb(oMaster, oCreature); + if(ai_IsInCombatRound(oCreature)) ai_ClearCombatState(oCreature); + ai_ClearCreatureActions(TRUE); + ai_SendMessages(GetName(oCreature) + " has went into hold mode after seeing a trap!", AI_COLOR_YELLOW, oMaster); + return TRUE; + } + } + if(ai_GetAIMode(oCreature, AI_MODE_PICKUP_ITEMS)) + { + if(GetLocalInt(oTrap, "AI_SAW_TRAP_" + sTag) && !bForce) return FALSE; + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oTrap) + " is trapped!", TRUE)); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oTrap, "AI_SAW_TRAP_" + sTag, TRUE); + } + return FALSE; +} +int ai_AttemptToByPassLock(object oCreature, object oLocked, int bForce = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "1446", "Attempting to bypass lock on " + + GetName(oLocked) + " [AI_OBJECT_IN_USE: " + + IntToString(GetLocalInt(oLocked, AI_OBJECT_IN_USE)) + "]" + + " bForce: " + IntToString(bForce)); + if(GetLocalInt(oLocked, AI_OBJECT_IN_USE)) return FALSE; + string sTag = GetTag(oCreature); + // Attempt to cast knock because its always safe to cast it, even on a trapped object. + if(ai_AttempToCastKnockSpell(oLocked, oCreature)) return TRUE; + // First, let's see if we notice that it's trapped + if(GetTrapDetectedBy(oCreature, oLocked)) + { + // Ick! Try and disarm the trap first + if(ai_ReactToTrap(oCreature, oLocked, bForce)) return TRUE; + } + if(GetLockKeyRequired(oLocked)) + { + // We might be able to open this. + string sKeyTag = GetLockKeyTag(oLocked); + // Do we have the key? + object oKey = ai_GetCreatureHasItem(oCreature, sKeyTag, FALSE); + if(AI_DEBUG) ai_Debug("0i_actions", "1469", "Requires a Key! sKeyTag: " + + sKeyTag + " Has key oKey: " + GetName(oKey)); + if(oKey != OBJECT_INVALID) + { + int nObjectType = GetObjectType(oLocked); + if(nObjectType == OBJECT_TYPE_DOOR) return ai_AttemptToOpenDoor(oCreature, oLocked, bForce); + else if (nObjectType == OBJECT_TYPE_PLACEABLE) + { + SetLocalInt(oLocked, AI_OBJECT_IN_USE, TRUE); + DelayCommand(18.0, DeleteLocalInt(oLocked, AI_OBJECT_IN_USE)); + AssignCommand(oCreature, ActionUnlockObject(oLocked)); + // Let them know we did it! + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 6, ":44:42:31:35:")); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oLocked, AI_OBJECT_IN_USE))); + // Continue checking for traps, locks, and loot. + AssignCommand(oCreature, ActionDoCommand(ai_ActionCheckNearbyObjects(oCreature))); + return TRUE; + } + } + else + { + if(GetLocalInt(oLocked, "AI_LOCKED_" + sTag) && !bForce) return FALSE; + // Let them know we can't get this done!. + AssignCommand(oCreature, ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oLocked) + " is special! It requires a special key to open."))); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oLocked, "AI_LOCKED_" + sTag, TRUE); + return FALSE; + } + } + if(bForce || ai_GetAIMode(oCreature, AI_MODE_PICK_LOCKS)) + { + // We must have ranks in open locks to actually open the lock! + if(GetSkillRank(SKILL_OPEN_LOCK, oCreature, TRUE)) + { + int nSkill = GetSkillRank(SKILL_OPEN_LOCK, oCreature); + int nLockDC = GetLockUnlockDC(oLocked); + object oPicks = ai_GetBestPicks(oCreature, nLockDC); + int nPickBonus = GetLocalInt(oPicks, "AI_BONUS"); + if(AI_DEBUG) ai_Debug("0i_actions", "1497", "I have picks: " + GetName(oPicks) + + " nSkill :" + IntToString(nSkill) + " nPickBonus: " + + IntToString(nPickBonus) + " + 20 = " + + IntToString(nSkill + nPickBonus + 20) + + " nLockDC: " + IntToString(nLockDC)); + if(nSkill + 20 + nPickBonus >= nLockDC) + { + SetLocalInt(oLocked, AI_OBJECT_IN_USE, TRUE); + DelayCommand(18.0, DeleteLocalInt(oLocked, AI_OBJECT_IN_USE)); + AssignCommand(oCreature, ai_ClearCreatureActions()); + AssignCommand(oCreature, ActionWait(1.0)); + AssignCommand(oCreature, ActionUseSkill(SKILL_OPEN_LOCK, oLocked, 0, oPicks)); + AssignCommand(oCreature, ActionWait(1.0)); + // Let them know we did it! + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":44:42:26:31:35:")); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oLocked, AI_OBJECT_IN_USE))); + // Continue checking for traps, locks, and loot. + AssignCommand(oCreature, ActionDoCommand(ai_ActionCheckNearbyObjects(oCreature))); + return TRUE; + } + else if(!GetLocalInt(oLocked, "AI_LOCKED_" + sTag)) + { + // Let them know we can't get this done! + AssignCommand(oCreature, ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "I'm not skilled enough to pick the lock on this " + GetName(oLocked) + "!", TRUE))); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oLocked, "AI_LOCKED_" + sTag, TRUE); + return FALSE; + } + } + } + if(bForce || ai_GetAIMode(oCreature, AI_MODE_BASH_LOCKS)) + { + //AssignCommand(oCreature, ai_ClearCreatureActions()); + // Check to make sure we are not using a ranged weapon. + if(!ai_GetIsRangeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature))) + { + if(ai_CheckClassType(oCreature, CLASS_TYPE_MONK)) ai_EquipBestMonkMeleeWeapon(oCreature); + else ai_EquipBestMeleeWeapon(oCreature); + AssignCommand(oCreature, ActionWait(1.0)); + if(ai_TryImprovedPowerAttackFeat(oCreature, oLocked)) return TRUE; + if(ai_TryPowerAttackFeat(oCreature, oLocked)) return TRUE; + if(ai_TryFlurryOfBlowsFeat(oCreature, oLocked)) return TRUE; + AssignCommand(oCreature, ActionAttack(oLocked)); + return TRUE; + } + if(GetLocalInt(oLocked, "AI_LOCKED_" + sTag) && !bForce) return FALSE; + // Let them know we can't get this done!. + AssignCommand(oCreature, ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "I cannot bash this " + GetName(oLocked) + " open!", TRUE))); + SetLocalInt(oLocked, "AI_LOCKED_" + sTag, TRUE); + return FALSE; + } + if(bForce || ai_GetAIMode(oCreature, AI_MODE_PICKUP_ITEMS)) + { + if(GetLocalInt(oLocked, "AI_LOCKED_" + sTag) && !bForce) return FALSE; + AssignCommand(oCreature, ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oLocked) + " is locked!", TRUE))); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oLocked, "AI_LOCKED_" + sTag, TRUE); + } + return FALSE; +} +int ai_AttemptToOpenDoor(object oCreature, object oDoor, int bForce = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_actions", "1542", "Attempting to open " + + GetName(oDoor) + " [AI_OBJECT_IN_USE: " + + IntToString(GetLocalInt(oDoor, AI_OBJECT_IN_USE)) + "] " + + " IsOpen: " + IntToString(GetIsOpen(oDoor)) + + " Plot: " + IntToString(GetPlotFlag(oDoor)) + "."); + if(!ai_GetAIMode(oCreature, AI_MODE_OPEN_DOORS) && !bForce) return FALSE; + if(GetLocalInt(oDoor, AI_OBJECT_IN_USE)) return FALSE; + if(GetIsOpen(oDoor)) return FALSE; + string sTag = GetTag(oCreature); + if(GetIsTrapped(oDoor)) + { + if(GetTrapDetectedBy(oDoor, GetMaster(oCreature))) SetTrapDetectedBy(oDoor, oCreature); + if(GetTrapDetectedBy(oDoor, oCreature)) + { + if(GetLocalInt(oDoor, "AI_SAW_TRAP_" + sTag)) return FALSE; + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oDoor) + " is trapped!", TRUE)); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oDoor, "AI_SAW_TRAP_" + sTag, TRUE); + return FALSE; + } + } + if(GetLocked(oDoor)) + { + if(GetLocalInt(oDoor, "AI_LOCKED_" + sTag)) return FALSE; + AssignCommand(oCreature, ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oDoor) + " is locked!", TRUE))); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oDoor, "AI_LOCKED_" + sTag, TRUE); + return FALSE; + } + SetLocalInt(oDoor, AI_OBJECT_IN_USE, TRUE); + DelayCommand(18.0, DeleteLocalInt(oDoor, AI_OBJECT_IN_USE)); + AssignCommand(oCreature, ActionOpenDoor(oDoor, TRUE)); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oDoor, AI_OBJECT_IN_USE))); + return TRUE; +} +void ai_ActionCheckNearbyObjects(object oCreature) +{ + if(ai_GetIsBusy(oCreature)) return; + ai_CheckNearbyObjects(oCreature); +} +int ai_CheckNearbyObjects(object oCreature) +{ + object oMaster = ai_GetPlayerMaster(oCreature); + location lMaster = GetLocation(oMaster); + int nObjectType, bIgnore; + int nFilter = OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_TRIGGER | OBJECT_TYPE_ITEM; + float fLockRange, fDoorRange, fLootRange, fObjectDistance; + float fTrapRange = GetLocalFloat(oCreature, AI_TRAP_CHECK_RANGE); + if(ai_GetAIMode(oCreature, AI_MODE_PICK_LOCKS) || + ai_GetAIMode(oCreature, AI_MODE_BASH_LOCKS)) fLockRange = GetLocalFloat(oCreature, AI_LOCK_CHECK_RANGE); + if(ai_GetAIMode(oCreature, AI_MODE_PICKUP_ITEMS)) fLootRange = GetLocalFloat(oCreature, AI_LOOT_CHECK_RANGE); + if(ai_GetAIMode(oCreature, AI_MODE_OPEN_DOORS)) fDoorRange = GetLocalFloat(oCreature, AI_OPEN_DOORS_RANGE); + if(AI_DEBUG && fTrapRange != 0.0) ai_Debug("0i_actions", "1579", " Checking " + FloatToString(fTrapRange, 0, 0) + " foot area for traps."); + if(AI_DEBUG && fLootRange != 0.0) ai_Debug("0i_actions", "1580", " Checking " + FloatToString(fLootRange, 0, 0) + " foot area for traps."); + if(AI_DEBUG && fLockRange != 0.0) ai_Debug("0i_actions", "1581", " Checking " + FloatToString(fLockRange, 0, 0) + " foot area for locks."); + if(AI_DEBUG && fDoorRange != 0.0) ai_Debug("0i_actions", "1582", " Checking " + FloatToString(fDoorRange, 0, 0) + " foot area for doors."); + float fLongestRange = fTrapRange; + vector vCreature = GetPositionFromLocation(GetLocation(oCreature)); + if(fLongestRange < fLootRange) fLongestRange = fLootRange; + if(fLongestRange < fLockRange) fLongestRange = fLockRange; + if(fLongestRange < fDoorRange) fLongestRange = fDoorRange; + object oObject = GetFirstObjectInShape(SHAPE_SPHERE, fLongestRange, lMaster, TRUE, nFilter); + while(oObject != OBJECT_INVALID) + { + fObjectDistance = GetDistanceBetween(oMaster, oObject); + if(AI_DEBUG) ai_Debug("0i_actions", "1651", "Checking Nearby Objects: " + + GetName(oObject) + " fDistance: " + FloatToString(fObjectDistance, 0, 2)); + if(GetTrapDetectedBy(oObject, oCreature)) + { + if(fTrapRange >= fObjectDistance) + { + if(ai_ReactToTrap(oCreature, oObject)) return TRUE; + } + } + if(GetLocked(oObject)) + { + if(fLockRange >= fObjectDistance) + { + if(ai_AttemptToByPassLock(oCreature, oObject)) return TRUE; + } + } + nObjectType = GetObjectType(oObject); + if(fDoorRange >= fObjectDistance && nObjectType == OBJECT_TYPE_DOOR) + { + if(ai_AttemptToOpenDoor(oCreature, oObject)) return TRUE; + } + if(fLootRange >= fObjectDistance) + { + if(nObjectType == OBJECT_TYPE_PLACEABLE) + { + if(!GetLocalInt(oObject, AI_OBJECT_IN_USE) && + ai_IsContainerLootable(oCreature, oObject)) + { + if(GetLocked(oObject)) + { + string sTag = GetTag(oCreature); + if(GetLocalInt(oObject, "AI_LOCKED_" + sTag)) return FALSE; + AssignCommand(oCreature, ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oObject) + " is locked!", TRUE))); + ActionDoCommand(ai_HaveCreatureSpeak(oCreature, 8, ":47:30:43:5:36:")); + SetLocalInt(oObject, "AI_LOCKED_" + sTag, TRUE); + return FALSE; + } + ai_ClearCreatureActions(); + ActionMoveToObject(oObject, TRUE); + AssignCommand(oCreature, ActionDoCommand(ai_SearchObject(oCreature, oObject, oMaster))); + return TRUE; + } + } + else if(nObjectType == OBJECT_TYPE_ITEM) + { + if(ai_ShouldIPickItUp(oCreature, oObject)) + { + ActionPickUpItem(oObject); + return TRUE; + } + } + } + oObject = GetNextObjectInShape(SHAPE_SPHERE, fLongestRange, lMaster, TRUE, nFilter); + } + return FALSE; +} +void ai_DetermineSpecialBehavior(object oCreature) +{ + object oTarget = ai_GetNearestEnemy(oCreature, 1, 7, 7, -1, -1, TRUE); + if(ai_GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE)) + { + if(ai_GetIsInCombat(oCreature)) ai_DoMonsterCombatRound(oTarget); + // * if not attacking, then wander. + else + { + AssignCommand(oCreature, ai_ClearCreatureActions()); + AssignCommand(oCreature, ActionRandomWalk()); + return; + } + } + else if(ai_GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE)) + { + if(GetIsObjectValid(ai_GetAttackedTarget(oCreature, TRUE, TRUE))) + { + if(oTarget != OBJECT_INVALID && GetDistanceBetween(oCreature, oTarget) <= 6.0) + { + if(!GetIsFriend(oTarget)) + { + if(GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0) + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_coward"); + ActionMoveAwayFromObject(oTarget, TRUE, AI_RANGE_LONG); + } + } + } + } + else if(!IsInConversation(OBJECT_SELF)) + { + AssignCommand(oCreature, ai_ClearCreatureActions()); + AssignCommand(oCreature, ActionRandomWalk()); + return; + } + } +} +//This function is used only because ActionDoCommand can only accept void functions +void ai_CreateSignPostNPC(string sTag, location lLocal) +{ + CreateObject(OBJECT_TYPE_CREATURE, sTag, lLocal); +} +void ai_ActivateFleeToExit(object oCreature) +{ + //minor optimizations - only grab these variables when actually needed + //can make for larger code, but it's faster + //object oExitWay = GetWaypointByTag("EXIT_" + GetTag(OBJECT_SELF)); + //location lLocal = GetLocalLocation(OBJECT_SELF, "NW_GENERIC_START_POINT"); + //string sTag = GetTag(OBJECT_SELF); + int nPlot = GetLocalInt(oCreature, "NW_GENERIC_MASTER"); + if(nPlot & NW_FLAG_TELEPORT_RETURN || nPlot & NW_FLAG_TELEPORT_LEAVE) + { + effect eVis = EffectVisualEffect(VFX_IMP_UNSUMMON); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oCreature); + if(nPlot & NW_FLAG_TELEPORT_RETURN) + { + location lLocal = GetLocalLocation(oCreature, "NW_GENERIC_START_POINT"); + string sTag = GetTag(oCreature); + DelayCommand(6.0, AssignCommand(oCreature, ActionDoCommand(ai_CreateSignPostNPC(sTag, lLocal)))); + } + AssignCommand(oCreature, ActionDoCommand(DestroyObject(oCreature, 0.75))); + } + else + { + if(nPlot & NW_FLAG_ESCAPE_LEAVE) + { + object oExitWay = GetWaypointByTag("EXIT_" + GetTag(oCreature)); + ActionMoveToObject(oExitWay, TRUE); + AssignCommand(oCreature, ActionDoCommand(DestroyObject(oCreature, 1.0))); + } + else if(nPlot & NW_FLAG_ESCAPE_RETURN) + { + string sTag = GetTag(oCreature); + object oExitWay = GetWaypointByTag("EXIT_" + sTag); + ActionMoveToObject(oExitWay, TRUE); + location lLocal = GetLocalLocation(oCreature, "NW_GENERIC_START_POINT"); + DelayCommand(6.0, AssignCommand(oCreature, ActionDoCommand(ai_CreateSignPostNPC(sTag, lLocal)))); + AssignCommand(oCreature, ActionDoCommand(DestroyObject(oCreature, 1.0))); + } + } +} +int ai_GetFleeToExit(object oCreature) +{ + int nPlot = GetLocalInt(oCreature, "NW_GENERIC_MASTER"); + if(nPlot & NW_FLAG_ESCAPE_RETURN) return TRUE; + else if(nPlot & NW_FLAG_ESCAPE_LEAVE) return TRUE; + else if(nPlot & NW_FLAG_TELEPORT_RETURN) return TRUE; + else if(nPlot & NW_FLAG_TELEPORT_LEAVE) return TRUE; + return FALSE; +} +void ai_ActionInitialization() +{ + SetAnimationCondition(NW_ANIM_FLAG_IS_ACTIVE); + SetAnimationCondition(NW_ANIM_FLAG_INITIALIZED); + SetLocalLocation(OBJECT_SELF, "ANIM_START_LOCATION", GetLocation(OBJECT_SELF)); +} +// Start interacting with a placeable object +void ai_ActionStartInteracting(object oPlaceable) +{ + SetAnimationCondition(NW_ANIM_FLAG_IS_INTERACTING); + ActionMoveToObject(oPlaceable, FALSE, DISTANCE_TINY); + ActionDoCommand(SetFacingPoint(GetPosition(oPlaceable))); + SetCurrentInteractionTarget(oPlaceable); + AnimActionPlayRandomInteractAnimation(oPlaceable); +} + +void ai_ActionStopInteracting() +{ + AnimActionRandomMoveAway(GetCurrentInteractionTarget(), DISTANCE_LARGE); + SetCurrentInteractionTarget(OBJECT_INVALID); + SetAnimationCondition(NW_ANIM_FLAG_IS_INTERACTING, FALSE); + AnimActionTurnAround(); + AnimActionPlayRandomAnimation(); +} + +// Start talking with a friend +void ai_ActionStartTalking(object oFriend, int nHDiff=0) +{ + object oMe = OBJECT_SELF; + ActionMoveToObject(oFriend, FALSE, DISTANCE_TINY); + AnimActionPlayRandomGreeting(nHDiff); + AssignCommand(oFriend, ActionMoveToObject(oMe, FALSE, DISTANCE_TINY)); + AssignCommand(oFriend, AnimActionPlayRandomGreeting(0 - nHDiff)); + SetCurrentFriend(oFriend); + AssignCommand(oFriend, SetCurrentFriend(oMe)); + ActionDoCommand(SetFacingPoint(GetPosition(oFriend))); + AssignCommand(oFriend, ActionDoCommand(SetFacingPoint(GetPosition(oMe)))); + SetAnimationCondition(NW_ANIM_FLAG_IS_TALKING); + SetAnimationCondition(NW_ANIM_FLAG_IS_TALKING, TRUE, oFriend); +} +void ai_ActionStopTalking(object oFriend, int nHDiff=0) +{ + object oMe = OBJECT_SELF; + AnimActionPlayRandomGoodbye(nHDiff); + AnimActionRandomMoveAway(oFriend, DISTANCE_LARGE); + AssignCommand(oFriend, AnimActionPlayRandomGoodbye(0 - nHDiff)); + AssignCommand(oFriend, AnimActionRandomMoveAway(oMe, DISTANCE_HUGE)); + SetAnimationCondition(NW_ANIM_FLAG_IS_TALKING, FALSE); + SetAnimationCondition(NW_ANIM_FLAG_IS_TALKING, FALSE, oFriend); +} +object ai_GetRandomFriend(float fMaxDistance) +{ + object oCreature = OBJECT_SELF; + location lStartLocation = GetLocalLocation(oCreature, "ANIM_START_LOCATION"); + object oFriend = GetNearestCreature(CREATURE_TYPE_REPUTATION, + REPUTATION_TYPE_FRIEND, + oCreature, d2(), + CREATURE_TYPE_PERCEPTION, + PERCEPTION_SEEN); + //SendMessageToPC(GetFirstPC(), " 0i_actions, 1748 oFriend: " + GetName(oFriend) + + // " AnimationCondition: " + IntToString(GetAnimationCondition(NW_ANIM_FLAG_IS_ACTIVE, oFriend)) + + // " Conversation: " + IntToString(IsInConversation(oFriend)) + + // " Combat: " + IntToString(GetIsInCombat(oFriend)) + + // " Distance: " + FloatToString(GetDistanceBetweenLocations(GetLocation(oFriend), lStartLocation), 0,0 )); + if(fMaxDistance > 20.0) fMaxDistance = 20.0; + if(GetIsObjectValid(oFriend) + && !GetIsPC(oFriend) + && !GetAnimationCondition(NW_ANIM_FLAG_IS_TALKING, oFriend) + && !IsInConversation(oFriend) + && !GetIsInCombat(oFriend) + && GetDistanceBetweenLocations(GetLocation(oFriend), lStartLocation) <= fMaxDistance) + { + return oFriend; + } + + return OBJECT_INVALID; +} +int ai_ActionFindFriend(float fMaxDistance) +{ + // Try and find a friend to talk to. + object oFriend = ai_GetRandomFriend(fMaxDistance); + //SendMessageToPC(GetFirstPC(), GetName(oFriend) + " TALKING: " + IntToString(GetAnimationCondition(NW_ANIM_FLAG_IS_TALKING, oFriend))); + if(GetIsObjectValid(oFriend) && !GetAnimationCondition(NW_ANIM_FLAG_IS_TALKING, oFriend)) + { + int nHDiff = GetHitDice(OBJECT_SELF) - GetHitDice(oFriend); + ai_ActionStartTalking(oFriend, nHDiff); + return TRUE; + } + return FALSE; +} +object ai_GetRandomObjectbyTag(string sTag, float fMaxDistance) +{ + int nIndex; + if(fMaxDistance < DISTANCE_MEDIUM) nIndex = d4(); + else if (fMaxDistance < DISTANCE_HUGE) nIndex = d8(); + else nIndex = d10(); + location lStartLocation = GetLocalLocation(OBJECT_SELF, "ANIM_START_LOCATION"); + if(fMaxDistance > 20.0) fMaxDistance = 20.0; + object oObject = GetNearestObjectToLocation(OBJECT_TYPE_PLACEABLE, lStartLocation, nIndex); + while(nIndex > 0) + { + if(GetTag(oObject) == sTag && + GetDistanceBetweenLocations(GetLocation(oObject), lStartLocation) <= fMaxDistance) break; + oObject = GetNearestObjectToLocation(OBJECT_TYPE_PLACEABLE, lStartLocation, --nIndex); + } + if(GetIsObjectValid(oObject)) + return oObject; + return OBJECT_INVALID; +} +int ai_ActionSitInChair(float fMaxDistance) +{ + object oChair = GetRandomObjectByTag("Chair", fMaxDistance); + if (GetIsObjectValid(oChair) && + !GetIsObjectValid(GetSittingCreature(oChair))) + { + ActionSit(oChair); + SetAnimationCondition(NW_ANIM_FLAG_IS_INTERACTING); + return TRUE; + } + return FALSE; +} +object ai_GetRandomUseableObject(float fMaxDistance) +{ + int nIndex; + if(fMaxDistance < DISTANCE_MEDIUM) nIndex = d2(); + else if (fMaxDistance < DISTANCE_HUGE) nIndex = d4(); + else nIndex = d6(); + location lStartLocation = GetLocalLocation(OBJECT_SELF, "ANIM_START_LOCATION"); + if(fMaxDistance > 20.0) fMaxDistance = 20.0; + object oObject = GetNearestObjectToLocation(OBJECT_TYPE_PLACEABLE, lStartLocation, nIndex); + while(nIndex > 0) + { + if(GetUseableFlag(oObject) && !GetLocked(oObject) && + GetDistanceBetweenLocations(GetLocation(oObject), lStartLocation) <= fMaxDistance) break; + oObject = GetNearestObjectToLocation(OBJECT_TYPE_PLACEABLE, lStartLocation, --nIndex); + } + if(GetIsObjectValid(oObject)) + return oObject; + return OBJECT_INVALID; +} +int ai_ActionFindPlaceable(float fMaxDistance) +{ + object oPlaceable = ai_GetRandomUseableObject(fMaxDistance); + if(GetIsObjectValid(oPlaceable)) + { + ai_ActionStartInteracting(oPlaceable); + return 1; + } + return 0; +} +int ai_ActionCheckDoor(float fMaxDistance) +{ + int nIndex = 1; + object oCreature = OBJECT_SELF; + location lStartLocation = GetLocalLocation(oCreature, "ANIM_START_LOCATION"); + if(fMaxDistance > 20.0) fMaxDistance = 20.0; + object oDoor = GetNearestObject(OBJECT_TYPE_DOOR, oCreature); + while(oDoor != OBJECT_INVALID) + { + if(GetDistanceBetweenLocations(GetLocation(oDoor), lStartLocation) <= fMaxDistance) + { + // Make sure everyone doesn't run to close or open the same door. + if(!GetLocalInt(oDoor, "DOOR_INTERACTION")) + { + if(GetIsOpen(oDoor)) + { + //SendMessageToPC(GetFirstPC(), GetName(oCreature) + + // " Closing " + GetName(oDoor) + "."); + SetLocalInt(oDoor, "DOOR_INTERACTION", TRUE); + ActionCloseDoor(oDoor); + AssignCommand(oDoor, ActionDoCommand(SetLocalInt(oDoor, "DOOR_INTERACTION", FALSE))); + return TRUE; + } + else if(GetLocalInt(GetModule(), AI_RULE_OPEN_DOORS)) + { + //SendMessageToPC(GetFirstPC(), GetName(oDoor) + " Locked: " + + // IntToString(GetLocked(oDoor)) + " Trapped: " + + // IntToString(GetIsTrapped(oDoor)) + + // " Plot: " + IntToString(GetPlotFlag(oDoor))); + if(!GetLocked(oDoor) && + !GetIsTrapped(oDoor) && + !GetPlotFlag(oDoor)) + { + //SendMessageToPC(GetFirstPC(), GetName(oCreature) + + // " Opening " + GetName(oDoor) + "."); + SetLocalInt(oDoor, "DOOR_INTERACTION", TRUE); + ActionOpenDoor(oDoor); + // If a door has been opened lets not go right behind and close for a minute. + DelayCommand(60.0, SetLocalInt(oDoor, "DOOR_INTERACTION", FALSE)); + return TRUE; + } + } + } + } + oDoor = GetNearestObject(OBJECT_TYPE_DOOR, oCreature, ++nIndex); + } + return FALSE; +} +int ai_ActionInteraction() +{ + // If we're talking, either keep going or stop. + // Low prob of stopping, since both parties have + // a chance and conversations are cool. + if(GetAnimationCondition(NW_ANIM_FLAG_IS_TALKING)) + { + object oFriend = GetCurrentFriend(); + //SendMessageToPC(GetFirstPC(), GetName(OBJECT_SELF) + " Is talking to " + GetName(oFriend)); + int nHDiff = GetHitDice(OBJECT_SELF) - GetHitDice(oFriend); + if(Random(100) < 20) + { + //SendMessageToPC(GetFirstPC(), GetName(OBJECT_SELF) + " I'm done talking!"); + ai_ActionStopTalking(oFriend, nHDiff); + return TRUE; + } + AnimActionPlayRandomTalkAnimation(nHDiff); + return TRUE; + } + // If we're interacting with a placeable, either keep going or stop. + // High probability of stopping, since looks silly to + // constantly turn something on-and-off. + if(GetAnimationCondition(NW_ANIM_FLAG_IS_INTERACTING)) + { + //SendMessageToPC(GetFirstPC(), GetName(OBJECT_SELF) + " Is interacting."); + if(Random(100) < 40) + { + //SendMessageToPC(GetFirstPC(), GetName(OBJECT_SELF) + " I'm done interacting!"); + ai_ActionStopInteracting(); + return TRUE; + } + AnimActionPlayRandomInteractAnimation(GetCurrentInteractionTarget()); + return TRUE; + } + return FALSE; +} +location ai_GetWalkingLocation(object oSource, float fDistance) +{ + location lStart = GetLocation(oSource); + // Try to move in a north/south/east/west direction that will allow better + // movement around the map! + float fFacing = GetFacing(oSource); + if(Random(100) < 25) fFacing = IntToFloat(Random(360)); + float fAngle; + if(fFacing > 315.0 || fFacing < 45.0) fAngle = DIRECTION_EAST; + else if(fFacing < 135.0) fAngle = DIRECTION_NORTH; + else if(fFacing < 225.0) fAngle = DIRECTION_WEST; + else fAngle = DIRECTION_SOUTH; + fAngle += IntToFloat(Random(20) - 10); + float fOrientation = fAngle; + return GenerateNewLocationFromLocation(lStart, fDistance, fAngle, fOrientation); +} +void ai_ActionRandomWalk(float fMaxDistance) +{ + // If we stay within our alloted distance then we can walk to the new location. + location lStartLocation = GetLocalLocation(OBJECT_SELF, "ANIM_START_LOCATION"); + int nRandom = FloatToInt(fMaxDistance); + if(nRandom > 20) nRandom = 20; + float fRandom = IntToFloat(Random(nRandom) + 1); + location lNewLocation = ai_GetWalkingLocation(OBJECT_SELF, fRandom); + if(AI_DEBUG) ai_Debug("0i_actions", "2092", GetName(OBJECT_SELF) + " is walking " + + FloatToString(GetDistanceBetweenLocations(lNewLocation, lStartLocation), 0, 0) + + " distance of fMaxDistance: " + FloatToString(fMaxDistance, 0, 0)); + ActionMoveToLocation(lNewLocation); +} +void ai_Actions() +{ + float fMaxDistance = GetLocalFloat(GetModule(), AI_RULE_WANDER_DISTANCE); + // Are we interacting? If so continue else see what else there is to do. + if(ai_ActionInteraction()) return; + // If we got here, we're not busy + ClearAllActions(); + // Check for chance to do an action to keep things interesting. + int nRoll = Random(100); + if(fMaxDistance < 2.0) + { + if(nRoll < 51) AnimActionPlayRandomAnimation(); + return; + } + int nRace = GetRacialType(OBJECT_SELF); + if(nRace != RACIAL_TYPE_ABERRATION && nRace != RACIAL_TYPE_ANIMAL && + nRace != RACIAL_TYPE_BEAST && nRace != RACIAL_TYPE_MAGICAL_BEAST && + nRace != RACIAL_TYPE_OOZE && nRace != RACIAL_TYPE_VERMIN) + { + if(nRoll < 5) if(ai_ActionSitInChair(fMaxDistance)) return; + // Open or close a door + if(nRoll < 20) if(ai_ActionCheckDoor(fMaxDistance)) return; + // Fiddle with a placeable + if(nRoll < 40) if(ai_ActionFindPlaceable(fMaxDistance)) return; + // Start talking to a friend + if(nRoll < 50) if(ai_ActionFindFriend(fMaxDistance)) return; + } + // Lets walk around. + if(nRoll < 80) + { + ai_ActionRandomWalk(fMaxDistance); + return; + } + // If we find nothing interesting to do then just stay put and look interesting. + AnimActionPlayRandomAnimation(); +} +int ai_CheckCurrentAction() +{ + int nAction = GetCurrentAction(); + if(nAction == ACTION_SIT) + { + // low prob of getting up, so we don't bop up and down constantly + if (Random(10) == 0) + { + AnimActionGetUpFromChair(); + return TRUE; + } + } + else if(nAction != ACTION_INVALID) + { + // Sometimes we cannot do an action so lets break out sometimes. + if((nAction == ACTION_CLOSEDOOR || + nAction == ACTION_OPENDOOR || + nAction == ACTION_MOVETOPOINT) && Random(100) < 20) return FALSE; + // we're doing *something*, don't switch + //AnimDebug("performing action"); + return TRUE; + } + return FALSE; +} +void ai_AmbientAnimations() +{ + if(!GetAnimationCondition(NW_ANIM_FLAG_INITIALIZED)) ai_ActionInitialization(); + // Check if we should turn off + if(!CheckIsAnimActive(OBJECT_SELF)) return; + // Check current actions so we don't interrupt something in progress + if(ai_CheckCurrentAction()) return; + // First check: go back to starting position and rest if we are hurt + //if(AnimActionRest()) return; + // If we get here then lets go see what we can do! + ai_Actions(); +} diff --git a/_module/nss/0i_associates.nss b/_module/nss/0i_associates.nss new file mode 100644 index 00000000..23091603 --- /dev/null +++ b/_module/nss/0i_associates.nss @@ -0,0 +1,2192 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0i_associates + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Scripts used for Associates. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +#include "nw_inc_gff" +// Return TRUE if oCreature can attack based on current modes and actions. +int ai_CanIAttack(object oCreature); +// Returns the nearest locked object from oMaster. +object ai_GetNearestLockedObject(object oCreature); +// Will look for the oTarget or go to the oSpeaker depending on the situation. +void ai_FindTheEnemy(object oCreature, object oSpeaker, object oTarget, int bMonster); +// Selects the correct response base on nCommand from oCommander. +// These are given from either a radial menu option or voice command. +void ai_SelectAssociateCommand(object oCreature, object oCommander, int nCommand); +// Set nAction for the caller to pass to their associates. i.e. For henchmans summons. +void ai_PassActionToAssociates(object oCreature, int nAction, int bStatus = TRUE); +// Set Set the AI Mode to oAssociate and their associates. +void ai_PassAIModeToAssociates(object oAssociate, int nAIMode, int bStatus = TRUE); +// Set oCreature's ai scripts based on its first class or the variable "AI_DEFAULT_SCRIPT". +// bSetBasicAIScript set to TRUE will skip defensive and ambush tactic type scripts. +void ai_SetAssociateAIScript(object oCreature, int bCheckTacticScripts = TRUE); +// Returns TRUE if oCreature can speak. +int ai_CanISpeak(object oCreature); +// Cleansup any henchman actions and then removes them from the PC's faction. +void ai_FireHenchman(object oPC, object oHenchman); +// Will cast defensive spells (Buffs) on oPC's party from oCreature. +void ai_HenchmanCastDefensiveSpells(object oCreature, object oPC); +// Returns TRUE if we are starting combat due to an enemy being near. +// This should be checked after any "is in combat" checks. +int ai_CheckForCombat(object oCreature, int bMonster); +// Checks all perceived creatures to see if we should calculate a combat round +// or start combat for Associates. +void ai_AssociateEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception); +// Checks all perceived creatures to see if we should calculate a combat round +// or start combat for Monsters. +void ai_MonsterEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception); +// Copies all int, float, and string variables from oOldObject to oNewObject. +void ai_CopyObjectVariables(object oOldObject, object oNewObject); +//****************************************************************************** +//********************* Creature event scripts ********************************* +//****************************************************************************** + +// Add to nw_ch_aca OnRested event script of henchman. +void ai_OnRested(object oCreature); + +//****************************************************************************** +//******************* Associate AI option scripts ****************************** +//****************************************************************************** + +// Increments/Decrements the following distance of associates. +void ai_FollowIncrement(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. +void ai_Ignore_Associates(object oPC, object oAssociate, string sAssociateType); +// Turns on/off Ignore floor traps for oAssociate. +void ai_Ignore_Traps(object oPC, object oAssociate, string sAssociateType); +// Turns on/off Search for oAssociate. +void ai_Search(object oPC, object oAssociate, string sAssociateType); +// Turns on/off Stealth for oAssociate. +void ai_Stealth(object oPC, object oAssociate, string sAssociateType); +// Turns on/off Open Doors for oAssociate. +void ai_OpenDoor(object oPC, object oAssociate, string sAssociateType); +// Turns on/off Picking/Bashing locks for oAssociate. +void ai_Locks(object oPC, object oAssociate, string sAssociateType, int nMode); +// Turns on/off Disarming of Traps for oAssociate. +void ai_Traps(object oPC, object oAssociate, string sAssociateType); +// Turns on/off the amount of speaking for oAssociate. +void ai_ReduceSpeech(object oPC, object oAssociate, string sAssociateType); +// Turns on/off use of offensive/defensive spells. +void ai_UseOffensiveMagic(object oPC, object oAssociate, int bDefensive, int bOffensive, string sAssociateType); +// Turns on/off magic use. +void ai_UseMagic(object oPC, object oAssociate, string sAssociateType); +// Turn Magic Item use on/off for oAssociates. +void ai_UseMagicItems(object oPC, object oAssociate, string sAssociateType); +// Adjusts loot options for oAssociate +void ai_Loot(object oPC, object oAssociate, string sAssociateType); +// Adjust loot options for oAssociate +void ai_Spontaneous(object oPC, object oAssociate, string sAssociateType); +// Increments/Decrements the magic use variable for the AI. +void ai_MagicIncrement(object oPC, object oAssociate, int nIncrement, string sAssociateType); +// Increments/Decrements the Loot Range use variable for the AI. +void ai_LootRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType); +// Increments/Decrements the Lock Range use variable for the AI. +void ai_LockRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType); +// Increments/Decrements the Trap Range use variable for the AI. +void ai_TrapRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType); +// Increments/Decrements the Open Door Range use variable for the AI. +void ai_OpenDoorIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType); +// Saves a new AI script for oAssociate. +void ai_SaveAIScript(object oPC, object oAssociate, int nToken); +// Button action for buffing a PC. +void ai_Buff_Button(object oPC, object oAssociate, int nOption, string sAssociateType); +// Button action for setting healing ranges. +void ai_Heal_Button(object oPC, object oAssociate, int nIncrement, string sVar, string sAssociateType); +// Button action for turning healing on/off. +void ai_Heal_OnOff(object oPC, object oAssociate, string sAssociateType, int nMode); +// Button action for selecting a target to follow. +void ai_FollowTarget(object oPC, object oAssociate); +// Code to make oCreature guard oMaster. +void ai_Philos_Guard(object oMaster, object oCreature); +// Code to make OBJECT_SELF follow oMaster. +void ai_Philos_Follow(object oMaster); +// Code to make OBJECT_SELF hold at their location. +void ai_Philos_StandGround(object oMaster); +// Code to make oCreature attack the nearest enemy. +void ai_Philos_AttackNearest(object oMaster, object oCreature); +// Code to make oCreature turn search mode on. +void ai_Philos_SetSearch(object oMaster, object oCreature, string sAssociateType, int bTurnOn); +// Code to make oCreature turn stealth mode on. +void ai_Philos_SetStealth(object oMaster, object oCreature, string sAssociateType, int bTurnOn); +// 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); +// 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. +void ai_HavePCPlaceTrap(object oPC, object oAssociate); +// Jumps oAssociate to oPC, if oPC == oAssociate it jumps all oAssocites to oPC. +void ai_JumpToPC(object oPC, object oAssociate); +// Allow oAssociate to use no clipping. +void ai_GhostMode(object oPC, object oAssociate, int nToken, string sAssociateType); +// Changes the camera view from either the player to the associate or back. +void ai_ChangeCameraView(object oPC, object oAssociate); +// Checks that the oAssociate is within sight and then opens the inventory. +void ai_OpenInventory(object oAssociate, object oPC); +// Executes an installed plugin. +void ai_Plugin_Execute(object oPC, string sElem, int bUser = 0); + +int ai_CanIAttack(object oCreature) +{ + if(AI_DEBUG) ai_Debug("0i_associate", "122", "Can I attack? Hold mode: " + + IntToString(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) + + " Follow mode: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_FOLLOW)) + + " Action (19/4): " + IntToString(GetCurrentAction(oCreature))); + if(ai_GetIsCharacter(oCreature)) return TRUE; + int nAction = GetCurrentAction(oCreature); + return (!ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND) && + !ai_GetAIMode(oCreature, AI_MODE_FOLLOW) && + nAction != ACTION_ITEMCASTSPELL && + nAction != ACTION_CASTSPELL); +} +object ai_GetNearestLockedObject(object oCreature) +{ + int nCnt = 1; + object oMaster = GetMaster(oCreature); + float fRange = GetLocalFloat(oCreature, AI_TRAP_CHECK_RANGE); + location lCreature = GetLocation(oCreature); + object oObject = GetNearestObjectToLocation(OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, lCreature, nCnt); + while (oObject != OBJECT_INVALID || GetDistanceBetween(oMaster, oObject) > fRange) + { + if(GetLocked(oObject) && ai_GetIsInLineOfSight(oMaster, oObject)) return oObject; + oObject = GetNearestObjectToLocation(OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, lCreature, ++nCnt); + } + return OBJECT_INVALID; +} +void ai_FindTheEnemy(object oCreature, object oSpeaker, object oTarget, int bMonster) +{ + if(GetLocalInt(oCreature, AI_AM_I_SEARCHING)) return; + if(oSpeaker == oTarget && d100() < 34) + { + // Let them know we heard something in the distance!. + if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK)) + { + string sSpeak = "I heard something!"; + int nRoll = d8(); + if(nRoll == 1) sSpeak = "Did you hear that?"; + if(nRoll == 2) sSpeak = "What was that noise?"; + if(nRoll == 3) sSpeak = "Something is moving."; + if(nRoll == 4) sSpeak = "Lookout! I heard a noise."; + if(nRoll == 5) sSpeak = "Listen! We have company."; + AssignCommand(oCreature, ai_HaveCreatureSpeak(oCreature, 0, sSpeak)); + } + ai_HaveCreatureSpeak(oCreature, 8, ":43:6:9:10:23:42:"); + } + if(GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_a_peaceful" || + GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_coward") return; + float fDistance, fPerceptionDistance; + if(bMonster) + { + // Check distance from the creature hearing this and the target. + fDistance = GetDistanceBetween(oCreature, oTarget); + fPerceptionDistance = GetLocalFloat(GetModule(), AI_RULE_PERCEPTION_DISTANCE); + } + else + { + // We want to use the distance between the PC and target not us. + fDistance = GetDistanceBetween(GetMaster(), oTarget); + fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); + } + if(AI_DEBUG) ai_Debug("0i_associates", "175", " fDistance: " + FloatToString(fDistance, 0, 2) + + " fPerceptionDistance: " + FloatToString(fPerceptionDistance, 0, 2) + + " Hiding? " + IntToString(GetStealthMode(oTarget))); + if(fDistance <= fPerceptionDistance) + { + SetLocalInt(oCreature, AI_AM_I_SEARCHING, TRUE); + if(LineOfSightObject(oCreature, oTarget)) + { + if(fDistance > AI_RANGE_CLOSE) + { + int bMoveForward = TRUE; + // 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(oTarget); + if(AI_DEBUG) ai_Debug("0i_associates", "189", GetName(oTarget) + " current action: " + IntToString(nAction)); + if(nAction == ACTION_MOVETOPOINT || + nAction == ACTION_INVALID || + nAction == ACTION_RANDOMWALK) bMoveForward = 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(oTarget)) bMoveForward = FALSE; + } + if(bMoveForward) + { + if(AI_DEBUG) ai_Debug("0i_associates", "201", "Running towards combat to engage " + GetName(oTarget)); + ActionMoveToObject(oTarget, TRUE, AI_RANGE_CLOSE); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING))); + return; + } + if(AI_DEBUG) ai_Debug("0i_associates", "207", "Searching for " + GetName(oTarget) + " while moving towards " + GetName(oSpeaker)); + SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE); + ActionMoveToObject(oSpeaker); + return; + } + 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); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING))); + return; + } + if(AI_DEBUG) ai_Debug("0i_associates", "218", "No line of sight for " + GetName(oTarget) + ". Moving towards " + GetName(oSpeaker)); + ActionMoveToObject(oSpeaker, TRUE); + AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING))); + } + +} +void ai_ReactToAssociate(object oCreature, object oCommander, int bMonster) +{ + object oTarget = GetLocalObject(oCommander, AI_MY_TARGET); + if (oTarget == OBJECT_INVALID) return; + if(ai_GetIsInCombat(oCreature)) + { + if(oCommander == GetMaster(oCreature) && ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) + { + ai_DoAssociateCombatRound(oCreature, oTarget); + } + else ai_DoAssociateCombatRound(oCreature); + return; + } + ai_FindTheEnemy(oCreature, oCommander, oTarget, bMonster); +} +void ai_SelectAssociateCommand(object oCreature, object oCommander, int nCommand) +{ + object oMaster = GetMaster(oCreature); + // These nCommands can be issued even when the caller is busy. + switch(nCommand) + { + // Master is being attacked by the enemy. + case ASSOCIATE_COMMAND_MASTERGOINGTOBEATTACKED: + { + object oAttacker = GetGoingToBeAttackedBy(oMaster); + if(AI_DEBUG) ai_Debug("0i_associate", "120", GetName(oMaster) + " has been attack by " + + GetName(GetGoingToBeAttackedBy(oMaster)) + "!"); + // Used to set who monsters are attacking. + int nAction = GetCurrentAction(oAttacker); + if(nAction == ACTION_ATTACKOBJECT) SetLocalObject(oAttacker, AI_ATTACKED_PHYSICAL, oMaster); + else if(nAction == ACTION_CASTSPELL || nAction == ACTION_ITEMCASTSPELL) + { + SetLocalObject(oAttacker, AI_ATTACKED_SPELL, oMaster); + } + if(!ai_GetIsBusy(oCreature) && ai_CanIAttack(oCreature)) + { + if(ai_GetIsInCombat(oCreature)) ai_DoAssociateCombatRound(oCreature); + else ai_FindTheEnemy(oCreature, oCommander, oAttacker, FALSE); + } + return; + } + // Menu used by a player to have the henchman follow them. + case ASSOCIATE_COMMAND_FOLLOWMASTER: + { + if(AI_DEBUG) ai_Debug("0i_associate", "135", GetName(oMaster) + " has commanded " + + GetName(oCreature) + " to FOLLOW."); + AssignCommand(oCreature, ai_Philos_Follow(oMaster)); + return; + } + // Menu used by a player to have the henchman go into NORMAL MODE. + // We also attack the nearest, this keeps henchman going into combat quickly. + case ASSOCIATE_COMMAND_ATTACKNEAREST: + { + if(AI_DEBUG) ai_Debug("0i_associates", "158", GetName(oMaster) + " has commanded " + + GetName(oCreature) + " to attack nearest(NORMAL MODE)."); + ai_Philos_AttackNearest(oMaster, oCreature); + return; + } + // Menu used by a player to have the henchman stay where they are standing. + case ASSOCIATE_COMMAND_STANDGROUND: + { + if(AI_DEBUG) ai_Debug("0i_associate", "189", GetName(oMaster) + " has commanded " + + GetName(OBJECT_SELF) + " to STANDGROUND."); + AssignCommand(oCreature, ai_Philos_StandGround(oMaster)); + return; + } + // Menu used by a player to have the henchman attack anyone who attacks them. + case ASSOCIATE_COMMAND_GUARDMASTER: + { + if(AI_DEBUG) ai_Debug("0i_associate", "211", GetName(oMaster) + " has commanded " + + GetName(oCreature) + " to GAURDMASTER."); + ai_Philos_Guard(oMaster, oCreature); + return; + } + // Menu used by a player to have the henchman heal them as soon as possible. + case ASSOCIATE_COMMAND_HEALMASTER: + { + // Player will be stuck with this variable if they are not using the AI. + DeleteLocalInt(oCommander, "AI_I_AM_BEING_HEALED"); + if(ai_GetIsInCombat(oCreature)) ai_TryHealingTalent(oCreature, ai_GetNumOfEnemiesInRange(oCreature), oCommander); + else AssignCommand(oCreature, ai_ActionTryHealing(oCreature, oCommander)); + return; + } + // Menu used by a player to toggle a henchmans casting options. + case ASSOCIATE_COMMAND_TOGGLECASTING: + { + if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) + { + ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, FALSE); + ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, TRUE); + ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, FALSE); + ai_SendMessages(GetName(oCreature) + " will now cast defensive spells only.", AI_COLOR_GRAY, oCommander); + } + else if(ai_GetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING)) + { + ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, FALSE); + ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, FALSE); + ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, TRUE); + ai_SendMessages(GetName(oCreature) + " will now cast offensive spells only.", AI_COLOR_GRAY, oCommander); + } + else if(ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, FALSE); + ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, FALSE); + ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, FALSE); + ai_SendMessages(GetName(oCreature) + " will now cast any spell.", AI_COLOR_GRAY, oCommander); + } + else + { + ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, TRUE); + ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, FALSE); + ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, FALSE); + ai_SendMessages(GetName(oCreature) + " will not use any magic.", AI_COLOR_GRAY, oCommander); + } + aiSaveAssociateModesToDb(oMaster, oCreature); + return; + } + } + // If we are busy then these nCommands are ignored. + if(!ai_GetIsBusy(oCreature)) + { + // Respond to shouts from friendly non-PCs only. + if (ai_CanIAttack(oCreature)) + { + if(nCommand == AI_ALLY_IS_WOUNDED) + { + if(ai_TryHealing(oCreature, oCommander)) return; + } + else if(nCommand == AI_ALLY_IS_DISEASED || + nCommand == AI_ALLY_IS_POISONED || + nCommand == AI_ALLY_IS_WEAK) + { + if(ai_HealSickness(oCreature, oCommander, oMaster, nCommand)) return; + } + // A friend sees an enemy. If we are not in combat lets seek them out too! + else if(nCommand == AI_ALLY_SEES_AN_ENEMY || + nCommand == AI_ALLY_HEARD_AN_ENEMY) + { + if(AI_DEBUG) ai_Debug("0i_associates", "282", GetName(oCreature) + " receives notice that " + + GetName(oCommander) + " has seen1/heard2(" + IntToString(nCommand) + " an enemy: " + + GetName(GetLocalObject(oCommander, AI_MY_TARGET)) + "!"); + ai_ReactToAssociate(oCreature, oCommander, FALSE); + return; + } + // A friend is in combat. Make some checks to see if we should help. + else if(nCommand == AI_ALLY_ATKED_BY_WEAPON || + nCommand == AI_ALLY_ATKED_BY_SPELL) + { + if(AI_DEBUG) ai_Debug("0i_associates", "291", GetName(oCreature) + " receives notice that " + + GetName(oCommander) + " was attacked by an enemy!" + + GetName(GetLocalObject(oCommander, AI_MY_TARGET)) + "!"); + ai_ReactToAssociate(oCreature, oCommander, FALSE); + return; + } + else if(nCommand == AI_ALLY_IS_DEAD) + { // Nothing at the moment. + if(AI_DEBUG) ai_Debug("0i_associates", "298", GetName(oCreature) + " receives notice that " + + GetName(oCommander) + " has died!"); + return; + } + } + switch(nCommand) + { + case ASSOCIATE_COMMAND_MASTERATTACKEDOTHER: + { + if(AI_DEBUG) ai_Debug("0i_associate", "307", GetName(oMaster) + " has attacked!"); + if(ai_CanIAttack(oCreature)) + { + if(ai_GetIsInCombat(oCreature)) ai_DoAssociateCombatRound(oCreature); + else ai_FindTheEnemy(oCreature, oCommander, ai_GetAttackedTarget(oCommander, TRUE, TRUE), FALSE); + } + return; + } + // Master tried to open a door or chest that is locked. + case ASSOCIATE_COMMAND_MASTERFAILEDLOCKPICK: + { + // In command mode we let the player tell us what to do. + if(ai_CanIAttack(oCreature)) + { + object oLock = ai_GetNearestLockedObject(oMaster); + //Check and see if our master want's us to open locks. + if(ai_GetAIMode(oCreature, AI_MODE_PICK_LOCKS) || + ai_GetAIMode(oCreature, AI_MODE_BASH_LOCKS)) + { + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE); + ai_AttemptToByPassLock(oCreature, oLock); + } + } + return; + } + // Master saw a trap. + case ASSOCIATE_COMMAND_MASTERSAWTRAP: + { + // In command mode we let the player tell us what to do. + if(ai_CanIAttack(oCreature)) + { + object oTrap = GetLastTrapDetected(oMaster); + // Sometimes GetLastTrapDetected seems to fail. + if(oTrap == OBJECT_INVALID) oTrap = GetNearestTrapToObject(oMaster, TRUE); + //Check and see if our master want's us to disarm the trap. + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE); + SetTrapDetectedBy(oTrap, oCreature); + ai_ReactToTrap(oCreature, oTrap); + } + return; + } + // Menu used by a player to toggle henchmans search on and off. + case ASSOCIATE_COMMAND_TOGGLESEARCH: + { + int bTurnOn = !ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH); + string sAssociateType = ai_GetAssociateType(oMaster, oCreature); + ai_Philos_SetSearch(oMaster, oCreature, sAssociateType, bTurnOn); + return; + } + // Menu used by a player to toggle henchmans stealth on and off. + case ASSOCIATE_COMMAND_TOGGLESTEALTH: + { + int bTurnOn = !ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH); + string sAssociateType = ai_GetAssociateType(oMaster, oCreature); + ai_Philos_SetStealth(oMaster, oCreature, sAssociateType, bTurnOn); + return; + } + // Menu used by a player to have the henchman try to bypass the nearest lock. + case ASSOCIATE_COMMAND_PICKLOCK: + { + ai_SetAIMode(oCreature, AI_MODE_DEFEND_MASTER, FALSE); + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE); + ai_SetAIMode(oCreature, AI_MODE_FOLLOW, FALSE); + object oLock = ai_GetNearestLockedObject(oMaster); + // Clear locked variable incase we tried already. + string sID = ObjectToString(oCreature); + SetLocalInt(oLock, "AI_LOCKED_" + sID, FALSE); + ai_AttemptToByPassLock(oCreature, oLock); + aiSaveAssociateModesToDb(oMaster, oCreature); + return; + } + // Menu used by a player to have the henchman try to disarm the nearest trap. + case ASSOCIATE_COMMAND_DISARMTRAP: + { + ai_SetAIMode(oCreature, AI_MODE_DEFEND_MASTER, FALSE); + ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE); + ai_SetAIMode(oCreature, AI_MODE_FOLLOW, FALSE); + object oTrap = GetNearestTrapToObject(oMaster); + // Clear trapped variable incase we tried already. + string sID = ObjectToString(oCreature); + ai_ReactToTrap(oCreature, oTrap, TRUE); + aiSaveAssociateModesToDb(oMaster, oCreature); + return; + } + // Menu used by a player to open a henchmans inventory to give, move, or take. + case ASSOCIATE_COMMAND_INVENTORY: + { + if(AI_OPEN_INVENTORY) + { + ai_HaveCreatureSpeak(oCreature, 4, ":29:46:35:"); + OpenInventory(oCreature, oCommander); + } + // Can't look at an associate's inventory. + else + { + ai_HaveCreatureSpeak(oCreature, 6, ":47:30:36:8:48:"); + ai_SendMessages("You cannot open " + GetName(oCreature) + "'s inventory.", AI_COLOR_GRAY, oMaster); + } + return; + } + case ASSOCIATE_COMMAND_LEAVEPARTY: + { + if(AI_REMOVE_HENCHMAN_ON) + { + ai_ClearCreatureActions(); + ai_FireHenchman (GetPCSpeaker(), oCreature); + PlayVoiceChat (VOICE_CHAT_GOODBYE, oCreature); + } + } + } + } +} +void ai_PassActionToAssociates(object oCreature, int nAction, int bStatus = TRUE) +{ + int nAssociateType; + object oAssociate; + for(nAssociateType = 2; nAssociateType < 6; nAssociateType ++) + { + oAssociate = GetAssociate(nAssociateType); + if(oAssociate != OBJECT_INVALID) SetActionMode(oAssociate, nAction, bStatus); + } +} +void ai_PassToAssociate(object oAssociate, int nAIMode, int bStatus) +{ + ai_ClearCreatureActions(TRUE); + ai_SetAIMode(oAssociate, nAIMode, bStatus); +} +void ai_PassAIModeToAssociates(object oAssociate, int nAIMode, int bStatus = TRUE) +{ + ai_SetAIMode(oAssociate, nAIMode, bStatus); + int nAssociateType; + object oAssoc; + for(nAssociateType = 2; nAssociateType < 6; nAssociateType ++) + { + oAssoc = GetAssociate(nAssociateType, oAssociate); + if(oAssoc != OBJECT_INVALID) AssignCommand(oAssoc, ai_PassToAssociate(oAssoc, nAIMode, bStatus)); + } +} +void ai_SetAssociateAIScript(object oCreature, int bCheckTacticScripts = TRUE) +{ + string sCombatAI; + object oMaster = GetMaster(); + if(ai_GetIsCharacter(oMaster)) + { + string sAssociateType = ai_GetAssociateType(oMaster, oCreature); + json jAIData = ai_GetAssociateDbJson(oMaster, sAssociateType, "aidata"); + sCombatAI = JsonGetString(JsonArrayGet(jAIData, 8)); + } + else sCombatAI = GetLocalString(oCreature, AI_DEFAULT_SCRIPT); + int nAssociateType = GetAssociateType(oCreature); + if (nAssociateType == ASSOCIATE_TYPE_FAMILIAR && sCombatAI == "") + { + sCombatAI = "ai_a_default"; + } + else if(sCombatAI == "ai_coward") + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, sCombatAI); + return; + } + else if(bCheckTacticScripts && GetLocalInt(GetModule(), AI_RULE_AMBUSH)) + { + // They should have a skill ranks equal to their level + 1 to use a special AI. + int nSkillNeeded = GetHitDice(oCreature) + 1; + if(sCombatAI == "" || sCombatAI == "ai_a_ambusher") + { + // Ambusher: requires either Improved Invisibility or Invisibility. + if(GetHasSpell(SPELL_IMPROVED_INVISIBILITY, oCreature) || + GetHasSpell(SPELL_INVISIBILITY, oCreature)) + { + int bCast = ai_TryToCastSpell(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature); + if(!bCast) bCast = ai_TryToCastSpell(oCreature, SPELL_INVISIBILITY, oCreature); + if(bCast) + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_a_ambusher"); + return; + } + } + // Ambusher: Requires a Hide and Move silently skill equal to your level + 1. + else if(GetSkillRank(SKILL_HIDE, oCreature) >= nSkillNeeded && + GetSkillRank(SKILL_MOVE_SILENTLY, oCreature) >= nSkillNeeded) + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_a_ambusher"); + return; + } + } + // Defensive : requires Parry skill equal to your level or Expertise. + else if(sCombatAI == "ai_a_defensive" || + (sCombatAI == "" && + (GetSkillRank(SKILL_PARRY, oCreature) >= nSkillNeeded || + GetHasFeat(FEAT_EXPERTISE, oCreature) || + GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature)))) + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_a_defensive"); + return; + } + else if(sCombatAI == "ai_cntrspell" || GetHasSpell(SPELL_LESSER_DISPEL, oCreature) || + GetHasSpell(SPELL_DISPEL_MAGIC, oCreature) || GetHasSpell(SPELL_GREATER_DISPELLING, oCreature)) + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_cntrspell"); + return; + } + } + if(sCombatAI == "") + { + // Select the best ai for this henchmen based on class. + int nClass = GetClassByPosition(1, oCreature); + // If they have more than one class use the default ai. + if(GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID) sCombatAI = "ai_a_default"; + else if(nClass == CLASS_TYPE_BARBARIAN) sCombatAI = "ai_a_barbarian"; + else if(nClass == CLASS_TYPE_BARD) sCombatAI = "ai_a_bard"; + else if(nClass == CLASS_TYPE_CLERIC) sCombatAI = "ai_a_cleric"; + else if(nClass == CLASS_TYPE_DRUID) sCombatAI = "ai_a_druid"; + else if(nClass == CLASS_TYPE_FIGHTER) sCombatAI = "ai_a_fighter"; + else if(nClass == CLASS_TYPE_MONK) sCombatAI = "ai_a_monk"; + else if(nClass == CLASS_TYPE_PALADIN) sCombatAI = "ai_a_paladin"; + else if(nClass == CLASS_TYPE_RANGER) sCombatAI = "ai_a_ranger"; + else if(nClass == CLASS_TYPE_ROGUE) sCombatAI = "ai_a_rogue"; + else if(nClass == CLASS_TYPE_SORCERER) sCombatAI = "ai_a_sorcerer"; + else if(nClass == CLASS_TYPE_WIZARD) sCombatAI = "ai_a_wizard"; + //else if(nClass == CLASS_TYPE_ABERRATION) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_ANIMAL) sCombatAI = "ai_a_animal"; + //else if(nClass == CLASS_TYPE_CONSTRUCT) sCombatAI = "ai_a_animal"; + //else if(nClass == CLASS_TYPE_DRAGON) sCombatAI = "ai_a_dragon"; + //else if(nClass == CLASS_TYPE_ELEMENTAL) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_FEY) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_GIANT) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_HUMANOID) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_MAGICAL_BEAST) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_MONSTROUS) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_OOZE) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_OUTSIDER) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_UNDEAD) sCombatAI = "ai_a_default"; + //else if(nClass == CLASS_TYPE_VERMIN) sCombatAI = "ai_a_animal"; + else sCombatAI = "ai_a_default"; + } + if(AI_DEBUG) ai_Debug("0i_associates", "530", GetName(oCreature) + " is setting AI to " + sCombatAI); + SetLocalString(oCreature, AI_COMBAT_SCRIPT, sCombatAI); + SetLocalString(oCreature, AI_DEFAULT_SCRIPT, sCombatAI); +} +int ai_CanISpeak (object oCreature) +{ + int nRace = GetRacialType (oCreature); + if (nRace == RACIAL_TYPE_ANIMAL || nRace == RACIAL_TYPE_BEAST || + nRace == RACIAL_TYPE_CONSTRUCT || nRace == RACIAL_TYPE_OOZE) return FALSE; + return (GetAbilityScore (oCreature, ABILITY_INTELLIGENCE) > 7); +} +void ai_FireHenchman(object oPC, object oHenchman) +{ + if(oPC == OBJECT_INVALID || oHenchman == OBJECT_INVALID) return; + // Now double-check that this is actually our master + if(GetMaster(oHenchman) != oPC) return; + // Turn off stealth mode + SetActionMode(oHenchman, ACTION_MODE_STEALTH, FALSE); + // Remove the henchman + RemoveHenchman (oPC, oHenchman); + ChangeToStandardFaction(oHenchman, STANDARD_FACTION_DEFENDER); +} +void ai_HenchmanCastDefensiveSpells (object oCreature, object oPC) +{ + ai_CastBuffs(oCreature, 3, 0, oPC); +} +int ai_CheckForCombat(object oCreature, int bMonster) +{ + object oEnemy = ai_GetNearestEnemy(oCreature, 1, 7, 7, 7, 5, TRUE); + //object oEnemy = ai_GetNearestEnemy(oCreature, 1, -1, -1, -1, -1, TRUE); + if(AI_DEBUG) ai_Debug("0i_associate", "586", "Checking for Combat: oEnemy is " + GetName(oEnemy) + + " Distance: " + FloatToString(GetDistanceBetween(oEnemy, oCreature), 0, 2)); + if(oEnemy != OBJECT_INVALID) + { + float fPerceptionDistance, fDistance; + if(bMonster) + { + fDistance = GetDistanceBetween(oCreature, oEnemy); + fPerceptionDistance = GetLocalFloat(GetModule(), AI_RULE_PERCEPTION_DISTANCE); + } + else + { + // We want to use the distance between the PC and target not us. + object oMaster = GetMaster(); + if(oMaster != OBJECT_INVALID) fDistance = GetDistanceBetween(oMaster, oEnemy); + else fDistance = GetDistanceBetween(oCreature, oEnemy); + fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); + if(fPerceptionDistance == 0.0) fPerceptionDistance = 20.0; + } + if(fDistance < fPerceptionDistance) + { + ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:"); + SetLocalObject (oCreature, AI_MY_TARGET, oEnemy); + SpeakString(AI_I_SEE_AN_ENEMY, TALKVOLUME_SILENT_TALK); + if(bMonster) ai_StartMonsterCombat(oCreature); + else if(ai_CanIAttack(oCreature)) ai_StartAssociateCombat(oCreature); + return TRUE; + } + } + return FALSE; +} +void ai_AssociateEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception) +{ + if(!ai_CanIAttack(oCreature)) return; + int nAction = GetCurrentAction(oCreature); + if(AI_DEBUG) ai_Debug("0i_associates", "775", "Our current action: " + IntToString(nAction)); + switch(nAction) + { + // These actions are uninteruptable. + case ACTION_CASTSPELL : + case ACTION_ITEMCASTSPELL : + case ACTION_COUNTERSPELL : return; + // Might be doing a special action that is not a defined action. + case ACTION_INVALID : + { + int nCombatWait = GetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + if(AI_DEBUG) ai_Debug("0i_associate", "761", "Doing a special action (nCombatWait): " + IntToString(nCombatWait)); + if(nCombatWait) + { + if(ai_IsInCombatRound(oCreature, nCombatWait)) return; + DeleteLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + } + } + // We need to reevaluate combat during these actions when we see a new enemy. + //case ACTION_ATTACKOBJECT : + //case ACTION_MOVETOPOINT : + } + if(ai_GetIsInCombat(oCreature)) + { + object oTarget = ai_GetAttackedTarget(oCreature); + if(AI_DEBUG) ai_Debug("0i_associates", "775", "Should we recalculate our combat round? oTarget: " + GetName(oTarget) + + " oTarget Distance: " + FloatToString(GetDistanceBetween(oCreature, oTarget), 0, 2) + + " oLastPerceived Distance: " + FloatToString(GetDistanceBetween(oCreature, oLastPerceived), 0, 2)); + // If the LastPerceived is our target then don't recalculate. + if(oTarget == oLastPerceived) return; + // If we don't have a target or the lastperceived is closer than our + // target then recalculate. + if(oTarget == OBJECT_INVALID || + GetDistanceBetween(oCreature, oTarget) > GetDistanceBetween(oCreature, oLastPerceived)) + { + // We should clear any skill cooldowns that are at at max since that means they were skipped. + if(GetLocalInt(oCreature, "AI_EMPATHY_COOLDOWN") == AI_EMPATHY_COOLDOWN) + { DeleteLocalInt(oCreature, "AI_EMPATHY_COOLDOWN"); } + else if (GetLocalInt(oCreature, "AI_TAUNT_COOLDOWN") == AI_TAUNT_COOLDOWN) + { DeleteLocalInt(oCreature, "AI_EMPATHY_COOLDOWN"); } + ai_DoAssociateCombatRound(oCreature); + return; + } + // Lets only reevaluate combat if the new enemy is more powerful + // than the average enemies we already know about. + int nPower = ai_GetCharacterLevels(oLastPerceived) / 2; + int nEnemyPower = GetLocalInt(oCreature, AI_ENEMY_POWER) / (GetLocalInt(oCreature, AI_ENEMY_NUMBERS) + 1); + if(AI_DEBUG) ai_Debug("0i_associates", "797", " Is the new opponent more powerful? " + + GetName(oLastPerceived) + " nPower: " + IntToString(nPower) + + " nEnemyPower: " + IntToString(nEnemyPower)); + if(nEnemyPower < nPower) ai_DoAssociateCombatRound(oCreature); + return; + } + // Heard fires first, but Heard and Seen are both set at the same time. + // So lets skip the hearing code if they are also seen. + 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:"); + SetLocalObject (oCreature, AI_MY_TARGET, oLastPerceived); + SpeakString(sPerception, TALKVOLUME_SILENT_TALK); + ai_StartAssociateCombat(oCreature); + } + else ai_FindTheEnemy(oCreature, oLastPerceived, oLastPerceived, FALSE); +} +void ai_MonsterEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception) +{ + if(!ai_CanIAttack(oCreature)) return; + int nAction = GetCurrentAction(oCreature); + if(AI_DEBUG) ai_Debug("0i_associates", "672", "nAction: " + IntToString(nAction)); + switch(nAction) + { + // These actions are uninteruptable. + case ACTION_CASTSPELL : + case ACTION_ITEMCASTSPELL : + case ACTION_COUNTERSPELL : return; + // Might be doing a special action that is not a defined action. + case ACTION_INVALID : + { + int nCombatWait = GetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + if(AI_DEBUG) ai_Debug("0i_associates", "683", "nCombatWait: " + IntToString(nCombatWait)); + if(nCombatWait) + { + if(ai_IsInCombatRound(oCreature, nCombatWait)) return; + DeleteLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + } + } + // We need to reevaluate combat during these actions when we see a new enemy. + //case ACTION_ATTACKOBJECT : + //case ACTION_MOVETOPOINT : + } + if(ai_GetIsInCombat(oCreature)) + { + object oTarget = ai_GetAttackedTarget(oCreature); + if(AI_DEBUG) ai_Debug("0i_associates", "697", "oTarget: " + GetName(oTarget) + + " oTarget Distance: " + FloatToString(GetDistanceBetween(oCreature, oTarget), 0, 2) + + " oLastPerceived Distance: " + FloatToString(GetDistanceBetween(oCreature, oLastPerceived), 0, 2)); + // If the LastPerceived is our target then don't recalculate. + if(oTarget == oLastPerceived) return; + // If we don't have a target or the lastperceived is closer than our + // target then recalculate. + if(oTarget == OBJECT_INVALID || + GetDistanceBetween(oCreature, oTarget) > GetDistanceBetween(oCreature, oLastPerceived)) + { + ai_DoMonsterCombatRound(oCreature); + return; + } + // Now only reevaluate combat if the new enemy is more powerful + // than the average enemies we already know about. + int nPower = ai_GetCharacterLevels(oLastPerceived) / 2; + int nEnemyPower = GetLocalInt(oCreature, AI_ENEMY_POWER) / (GetLocalInt(oCreature, AI_ENEMY_NUMBERS) + 1); + if(AI_DEBUG) ai_Debug("0i_associates", "714", GetName(oLastPerceived) + " nPower: " + IntToString(nPower) + + " nEnemyPower: " + IntToString(nEnemyPower)); + if(nEnemyPower < nPower) ai_DoMonsterCombatRound(oCreature); + return; + } + if(sPerception == AI_I_SEE_AN_ENEMY) + { + if(d100() < 34) + { + // We are not in combat so alert our allies! + ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:"); + } + SetLocalObject(oCreature, AI_MY_TARGET, oLastPerceived); + SpeakString(sPerception, TALKVOLUME_SILENT_TALK); + ai_StartMonsterCombat(oCreature); + } + else ai_FindTheEnemy(oCreature, oLastPerceived, oLastPerceived, TRUE); +} +void ai_CopyObjectVariables(object oOldObject, object oNewObject) +{ + json jObject = ObjectToJson(oOldObject, TRUE); + json jVarTable = GffGetList(jObject, "VarTable"); + string sVariable, sName; + int nIndex, nVarType; + json jVar = JsonArrayGet(jVarTable, nIndex); + while(JsonGetType(jVar) != JSON_TYPE_NULL) + { + sName = JsonGetString(GffGetString(jVar, "Name")); + nVarType = JsonGetInt(GffGetDword(jVar, "Type")); + if(nVarType == 1) SetLocalInt(oNewObject, sName, JsonGetInt(GffGetInt(jVar, "Value"))); + else if(nVarType == 2) SetLocalFloat(oNewObject, sName, JsonGetFloat(GffGetFloat(jVar, "Value"))); + else if(nVarType == 3) SetLocalString(oNewObject, sName, JsonGetString(GffGetString(jVar, "Value"))); + jVar = JsonArrayGet(jVarTable, ++nIndex); + } +} +//****************************************************************************** +//********************* Creature event scripts ********************************* +//****************************************************************************** + +void ai_OnRested(object oCreature) +{ + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_AFTER_REST)) + { + int nLevel = ai_GetCharacterLevels(oCreature); + float fDelay = StringToFloat(Get2DAString("restduration", "DURATION", nLevel)); + fDelay = (fDelay / 1000.0f) + 2.0f; + DelayCommand(fDelay, ai_HenchmanCastDefensiveSpells(oCreature, GetMaster())); + } +} + +//****************************************************************************** +//******************* Associate AI option scripts ****************************** +//****************************************************************************** +void ai_UpdateToolTipUI(object oPC, string sWindowID1, string sWindowID2, string sToolTipBind, string sText) +{ + int nMenuToken = NuiFindWindow(oPC, sWindowID1); + if(nMenuToken) NuiSetBind (oPC, nMenuToken, sToolTipBind, JsonString (sText)); + if(sWindowID2 != "") + { + int nWidgetToken = NuiFindWindow(oPC, sWindowID2); + if(nWidgetToken) NuiSetBind (oPC, nWidgetToken, sToolTipBind, JsonString (sText)); + } +} +void ai_FollowIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType) +{ + float fAdjustment = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE) + fIncrement; + if(fAdjustment > 10.0) fAdjustment = 10.0; + else if(fAdjustment < 1.0) fAdjustment = 1.0; + SetLocalFloat(oAssociate, AI_FOLLOW_RANGE, fAdjustment); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 6, JsonFloat(fAdjustment)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + string sName; + object oTarget = GetLocalObject(oAssociate, AI_FOLLOW_TARGET); + string sTarget; + if(oTarget != OBJECT_INVALID) sTarget = GetName(oTarget); + else + { + if(ai_GetIsCharacter(oAssociate)) sTarget = "nobody"; + else sTarget = GetName(oPC); + } + float fRange = fAdjustment + + StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oAssociate))); + string sRange = FloatToString(fRange, 0, 0); + if(oPC == oAssociate) + { + sName = " All associates"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_follow_tooltip", sName + " enter follow mode "); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]"); + } + else + { + sName = " " + GetName(oAssociate); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_follow_tooltip", sName + " enter follow mode [" + sRange + " meters]"); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]"); + } +} +void ai_Ranged(object oPC, object oAssociate, string sAssociateType) +{ + //ai_ClearCreatureActions(); + if(ai_GetAIMode(oAssociate, AI_MODE_STOP_RANGED)) + { + ai_SendMessages(GetName(oAssociate) + " is using ranged combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ranged_tooltip", " Ranged On"); + ai_SetAIMode(oAssociate, AI_MODE_STOP_RANGED, FALSE); + ai_EquipBestRangedWeapon(oAssociate); + } + else + { + ai_SendMessages(GetName(oAssociate) + " is using melee combat only.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ranged_tooltip", " Ranged Off"); + ai_SetAIMode(oAssociate, AI_MODE_STOP_RANGED, TRUE); + ai_EquipBestMeleeWeapon(oAssociate); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_EquipWeapons(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF)) + { + ai_SendMessages(GetName(oAssociate) + " will be equiping their best weapons.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_equip_weapon_tooltip", " Equiping Best Weapons On"); + ai_SetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will not equip their best weapons.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_equip_weapon_tooltip", " Equiping Best Weapons Off"); + ai_SetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Search(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH)) + { + ai_SendMessages(GetName(oAssociate) + " is turning search off.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode Off"); + SetActionMode(oAssociate, ACTION_MODE_DETECT, FALSE); + ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " is turning search on.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode On"); + SetActionMode(oAssociate, ACTION_MODE_DETECT, TRUE); + ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Stealth(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH)) + { + ai_SendMessages(GetName(oAssociate) + " is turning stealth off.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode Off"); + SetActionMode(oAssociate, ACTION_MODE_STEALTH, FALSE); + ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " is turning stealth on.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode On"); + SetActionMode(oAssociate, ACTION_MODE_STEALTH, TRUE); + ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_OpenDoor(object oPC, object oAssociate, string sAssociateType) +{ + string sRange = FloatToString(GetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE), 0, 0); + if(ai_GetAIMode(oAssociate, AI_MODE_OPEN_DOORS)) + { + ai_SendMessages(GetName(oAssociate) + " is turning open doors off.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_open_door_tooltip", " Open Doors Off [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_OPEN_DOORS, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " is turning open doors on.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_open_door_tooltip", " Open Doors On [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_OPEN_DOORS, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Locks(object oPC, object oAssociate, string sAssociateType, int nMode) +{ + string sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE), 0, 0); + if(nMode == 1) + { + if(ai_GetAIMode(oAssociate, AI_MODE_PICK_LOCKS)) + { + ai_SendMessages(GetName(oAssociate) + " will stop picking locks.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_pick_locks_tooltip", " Pick Locks Off [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_PICK_LOCKS, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will now pick locks.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_pick_locks_tooltip", " Pick Locks On [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_PICK_LOCKS, TRUE); + } + } + else if(nMode == 2) + { + if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS)) + { + ai_SendMessages(GetName(oAssociate) + " will stop bashing.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_bash_locks_tooltip", " Bash Locks Off [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_BASH_LOCKS, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will now bash things.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_bash_locks_tooltip", " Bash Locks On [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_BASH_LOCKS, TRUE); + } + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Traps(object oPC, object oAssociate, string sAssociateType) +{ + string sRange = FloatToString(GetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE), 0, 0); + if(ai_GetAIMode(oAssociate, AI_MODE_DISARM_TRAPS)) + { + ai_SendMessages(GetName(oAssociate) + " will stop disarming traps.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_traps_tooltip", " Disable Traps Off [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_DISARM_TRAPS, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will now disarm traps.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_traps_tooltip", " Disable Traps On [" + sRange + " meters]"); + ai_SetAIMode(oAssociate, AI_MODE_DISARM_TRAPS, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_ReduceSpeech(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK)) + { + ai_SendMessages(GetName(oAssociate) + " will increase speech.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_quiet_tooltip", " Reduced Speech Off"); + ai_SetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will reduce speech.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_quiet_tooltip", " Reduced Speech On"); + ai_SetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_UseOffensiveMagic(object oPC, object oAssociate, int bDefensive, int bOffensive, string sAssociateType) +{ + if(bOffensive) + { + if(ai_GetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING)) + { + ai_SendMessages(GetName(oAssociate) + " has stopped using offensive magic in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_off_magic_tooltip", " Offensive Magic Off"); + ai_SetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " is now using offensive magic in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_off_magic_tooltip", " Offensive Magic On"); + ai_SetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING, TRUE); + } + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_def_magic_tooltip", " Defensive Magic Off"); + ai_SetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING, FALSE); + } + else if(bDefensive) + { + if(ai_GetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING)) + { + ai_SendMessages(GetName(oAssociate) + " has stopped using defensive magic in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_def_magic_tooltip", " Defensive Magic Off"); + ai_SetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " is now using defensive magic in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_def_magic_tooltip", " Defensive Magic On"); + ai_SetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING, TRUE); + } + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_off_magic_tooltip", " Offensive Magic Off"); + ai_SetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING, FALSE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_UseMagic(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC)) + { + ai_SendMessages(GetName(oAssociate) + " is now using magic in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_tooltip", " Magic On"); + ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " has stopped using magic in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_tooltip", " Magic Off"); + ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_UseMagicItems(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS)) + { + ai_SendMessages(GetName(oAssociate) + " is now using magic items in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_items_tooltip", " Magic Items On"); + ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " has stopped using magic items in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_items_tooltip", " Magic Items Off"); + ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Loot(object oPC, object oAssociate, string sAssociateType) +{ + int bLooting = !ai_GetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS); + string sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE), 0, 0); + string sMessage, sText; + if(bLooting) + { + sMessage = " is picking up items."; + sText = " Looting On [" + sRange + " meters]"; + } + else + { + sMessage = " is not picking up items."; + sText = " Looting Off [" + sRange + " meters]"; + } + ai_SendMessages(GetName(oAssociate) + sMessage, AI_COLOR_YELLOW, oPC); + ai_SetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS, bLooting); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_loot_tooltip", sText); + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Spontaneous(object oPC, object oAssociate, string sAssociateType) +{ + int bSpontaneous = !ai_GetMagicMode(oAssociate, AI_MAGIC_NO_SPONTANEOUS_CURE); + string sMessage, sText; + + if(bSpontaneous) + { + sMessage = " has stop casting spontaneous healing spells."; + sText = " Spontaneous casting Off"; + } + else + { + sMessage = " will now cast spontaneous healing spells."; + sText = " Spontaneous casting On"; + } + ai_SendMessages(GetName(oAssociate) + sMessage, AI_COLOR_YELLOW, oPC); + ai_SetMagicMode(oAssociate, AI_MAGIC_NO_SPONTANEOUS_CURE, bSpontaneous); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_spontaneous_tooltip", sText); + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_MagicIncrement(object oPC, object oAssociate, int nIncrement, string sAssociateType) +{ + int nAdjustment = GetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT) + nIncrement; + if(nAdjustment > 100) nAdjustment = 100; + else if(nAdjustment < -100) nAdjustment = -100; + SetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT, nAdjustment); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 0, JsonInt(nAdjustment)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + string sMagic = IntToString(nAdjustment); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_level_tooltip", " Magic Level [" + sMagic + "]"); +} +void ai_LootRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType) +{ + float fAdjustment = GetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE) + fIncrement; + if(fAdjustment > 40.0) fAdjustment = 40.0; + else if(fAdjustment < 0.0) fAdjustment = 0.0; + SetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE, fAdjustment); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 3, JsonFloat(fAdjustment)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + string sRange = FloatToString(fAdjustment, 0, 0); + string sLoot = " Looting Off [" + sRange + " meters]"; + if(ai_GetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS)) sLoot = " Looting On [" + sRange + " meters]"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_loot_tooltip", sLoot); +} +void ai_LockRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType) +{ + float fAdjustment = GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE) + fIncrement; + if(fAdjustment > 40.0) fAdjustment = 40.0; + else if(fAdjustment < 0.0) fAdjustment = 0.0; + SetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE, fAdjustment); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 4, JsonFloat(fAdjustment)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + string sRange = FloatToString(fAdjustment, 0, 0); + string sPick = " Pick Locks Off [" + sRange + " meters]"; + string sBash = " Bash Off [" + sRange + " meters]"; + if(ai_GetAIMode(oAssociate, AI_MODE_PICK_LOCKS)) sPick = " Pick Locks On [" + sRange + " meters]"; + if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS)) sBash = " Bash On [" + sRange + " meters]"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_pick_locks_tooltip", sPick); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_bash_locks_tooltip", sBash); +} +void ai_TrapRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType) +{ + float fAdjustment = GetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE) + fIncrement; + if(fAdjustment > 40.0) fAdjustment = 40.0; + else if(fAdjustment < 0.0) fAdjustment = 0.0; + SetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE, fAdjustment); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 5, JsonFloat(fAdjustment)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + string sRange = FloatToString(fAdjustment, 0, 0); + string sText = " Disable Traps Off [" + sRange + " meters]"; + if(ai_GetAIMode(oAssociate, AI_MODE_DISARM_TRAPS)) sText = " Disable Traps On [" + sRange + " meters]"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_traps_tooltip", sText); +} +void ai_OpenDoorIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType) +{ + float fAdjustment = GetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE) + fIncrement; + if(fAdjustment > 40.0) fAdjustment = 40.0; + else if(fAdjustment < 0.0) fAdjustment = 0.0; + SetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE, fAdjustment); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jAIData = JsonArraySet(jAIData, 9, JsonFloat(fAdjustment)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + string sRange = FloatToString(fAdjustment, 0, 0); + string sText = " Open Doors Off [" + sRange + " meters]"; + if(ai_GetAIMode(oAssociate, AI_MODE_OPEN_DOORS)) sText = " Open Doors On [" + sRange + " meters]"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_open_door_tooltip", sText); +} +void ai_SaveAIScript(object oPC, object oAssociate, int nToken) +{ + string sScript = JsonGetString(NuiGetBind(oPC, nToken, "txt_ai_script")); + string sOldScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + if(GetStringLeft(sScript, 5) != "ai_a_") ai_SendMessages(sScript + " does not have correct prefix it must have ai_a_ for associates! Did not change AI script.", AI_COLOR_RED, oPC); + else if(ResManGetAliasFor(sScript, RESTYPE_NCS) == "") + { + ai_SendMessages(sScript + " not found by ResMan! This is not a valid AI script.", AI_COLOR_RED, oPC); + } + else if(sScript != sOldScript) + { + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + if(JsonGetType(JsonArrayGet(jAIData, 8)) == JSON_TYPE_NULL) jAIData = JsonArrayInsert(jAIData, JsonString(sScript)); + else jAIData = JsonArraySet(jAIData, 8, JsonString(sScript)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + ai_SendMessages(GetName(oAssociate) + " is now using " + sScript + " AI script!", AI_COLOR_GREEN, oPC); + } + else ai_SendMessages(GetName(oAssociate) + " is already using this script! Did not change AI script.", AI_COLOR_RED, oPC); +} +void ai_Buff_Button(object oPC, object oAssociate, int nOption, string sAssociateType) +{ + if(nOption == 0) + { + int bRestBuff = !ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST); + ai_SetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST, bRestBuff); + if(bRestBuff) + { + ai_SendMessages(GetName(oAssociate) + " will cast long buffs after resting.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_buff_rest_tooltip", " [On] Turn buffing after resting off."); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will not cast long buffs after resting.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_buff_rest_tooltip", " [Off] Turn buffing after resting on."); + } + aiSaveAssociateModesToDb(oPC, oAssociate); + } + else + { + if(!GetIsPossessedFamiliar(oAssociate)) + { + object oEnemy = GetNearestEnemy(oAssociate); + //ai_Debug("0e_nui", "865", "oEnemy: " + GetName(oEnemy) + " fDistance: " + + // FloatToString(GetDistanceBetween(oAssociate, oEnemy), 0, 2)); + if(GetDistanceBetween(oAssociate, oEnemy) > 30.0 || + oEnemy == OBJECT_INVALID) + { + ai_CastBuffs(oAssociate, nOption, 0, oPC); + } + else ai_SendMessages("You cannot buff while there are enemies nearby.", AI_COLOR_RED, oPC); + } + else ai_SendMessages("You cannot buff while possessing your familiar.", AI_COLOR_RED, oPC); + } +} +void ai_Heal_Button(object oPC, object oAssociate, int nIncrement, string sVar, string sAssociateType) +{ + int nHeal = GetLocalInt(oAssociate, sVar); + if(nIncrement > 0 && nHeal > 100 - nIncrement) nHeal = 100 - nIncrement; + if(nIncrement < 0 && nHeal < abs(nIncrement)) nHeal = abs(nIncrement); + nHeal += nIncrement; + SetLocalInt(oAssociate, sVar, nHeal); + string sHeal = IntToString(nHeal); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + if(sVar == AI_HEAL_OUT_OF_COMBAT_LIMIT) + { + string sText = " Will heal at or below [" + sHeal + "%] health out of combat"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_heal_out_tooltip", sText); + jAIData = JsonArraySet(jAIData, 1, JsonInt(nHeal)); + } + else if(sVar == AI_HEAL_IN_COMBAT_LIMIT) + { + string sText = " Will heal at or below [" + sHeal + "%] health in combat"; + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_heal_in_tooltip", sText); + jAIData = JsonArraySet(jAIData, 2, JsonInt(nHeal)); + } + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); +} +void ai_Heal_OnOff(object oPC, object oAssociate, string sAssociateType, int nMode) +{ + string sText, sText2; + if(nMode == 1) + { + if(ai_GetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF)) + { + ai_SetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF, FALSE); + sText = " Self healing On"; + sText2 = " will now use healing on themselves."; + } + else + { + ai_SetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF, TRUE); + sText = " Self healing Off"; + sText2 = " will stop using healing on themselves."; + } + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_heals_onoff_tooltip", sText); + } + else + { + if(ai_GetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF)) + { + ai_SetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF, FALSE); + sText = " Party healing On"; + sText2 = " will now use healing on party members."; + } + else + { + ai_SetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF, TRUE); + sText = " Party healing Off"; + sText2 = " will stop using healing on party members."; + } + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_healp_onoff_tooltip", sText); + } + ai_SendMessages(GetName(oAssociate) + sText2, AI_COLOR_YELLOW, oPC); + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Cure_OnOff(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetMagicMode(oAssociate, AI_MAGIC_CURE_SPELLS_OFF)) + { + ai_SendMessages(GetName(oAssociate) + " will now cast cure spells.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cure_onoff_tooltip", " Cast Cure Spells On"); + ai_SetMagicMode(oAssociate, AI_MAGIC_CURE_SPELLS_OFF, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will stop casting cure spells.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cure_onoff_tooltip", " Cast Cure Spells Off"); + ai_SetMagicMode(oAssociate, AI_MAGIC_CURE_SPELLS_OFF, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Ignore_Associates(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES)) + { + ai_SendMessages(GetName(oAssociate) + " will stop ignoring henchman's associates and enemy associates.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_assoc_tooltip", " Ignore Enemy Associates Off"); + ai_SetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will now ignore henchman's associates and enemy associates.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_assoc_tooltip", " Ignore Enemy Associates On"); + ai_SetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_Ignore_Traps(object oPC, object oAssociate, string sAssociateType) +{ + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS)) + { + ai_SendMessages(GetName(oAssociate) + " will stop ignoring traps on the floor and will stop moving when one is seen.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_traps_tooltip", " Ignore Floor Traps Off"); + ai_SetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS, FALSE); + } + else + { + ai_SendMessages(GetName(oAssociate) + " will now ignore traps on the floor and will continue with their actions.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_traps_tooltip", " Ignore Floor Traps On"); + ai_SetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS, TRUE); + } + aiSaveAssociateModesToDb(oPC, oAssociate); +} +void ai_FollowTarget(object oPC, object oAssociate) +{ + SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate); + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_FOLLOW_TARGET"); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_Original_Guard() +{ + ResetHenchmenState(); + //Companions will only attack the Masters Last Attacker + SetAssociateState(NW_ASC_MODE_DEFEND_MASTER); + SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE); + object oMaster = GetMaster(); + object oLastAttacker = GetLastHostileActor(oMaster); + // * for some reason this is too often invalid. still the routine + // * works corrrectly + SetLocalInt(OBJECT_SELF, "X0_BATTLEJOINEDMASTER", TRUE); + HenchmenCombatRound(oLastAttacker); + ai_SendMessages(GetName(OBJECT_SELF) + " is now guarding you!", AI_COLOR_YELLOW, oMaster); +} +void ai_Original_Follow() +{ + ResetHenchmenState(); + SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE); + DelayCommand(2.5, VoiceCanDo()); + object oMaster = GetMaster(); + ActionForceFollowObject(oMaster, GetFollowDistance()); + SetAssociateState(NW_ASC_IS_BUSY); + DelayCommand(5.0, SetAssociateState(NW_ASC_IS_BUSY, FALSE)); + ai_SendMessages(GetName(OBJECT_SELF) + " is now following You!", AI_COLOR_YELLOW, oMaster); +} +void ai_Original_StandGround() +{ + SetAssociateState(NW_ASC_MODE_STAND_GROUND); + SetAssociateState(NW_ASC_MODE_DEFEND_MASTER, FALSE); + DelayCommand(2.0, VoiceCanDo()); + ActionAttack(OBJECT_INVALID); + ClearActions(CLEAR_X0_INC_HENAI_RespondToShout1); + ai_SendMessages(GetName(OBJECT_SELF) + " is now standing their ground!", AI_COLOR_YELLOW, GetMaster()); +} +void ai_Original_AttackNearest() +{ + ResetHenchmenState(); + SetAssociateState(NW_ASC_MODE_DEFEND_MASTER, FALSE); + SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE); + DetermineCombatRound(); + // * bonus feature. If master is attacking a door or container, issues VWE Attack Nearest + // * will make henchman join in on the fun + object oMaster = GetMaster(); + object oTarget = GetAttackTarget(oMaster); + if (GetIsObjectValid(oTarget) == TRUE) + { + if (GetObjectType(oTarget) == OBJECT_TYPE_PLACEABLE || GetObjectType(oTarget) == OBJECT_TYPE_DOOR) + { + ActionAttack(oTarget); + } + } + ai_SendMessages(GetName(OBJECT_SELF) + " is now in normal mode!", AI_COLOR_YELLOW, oMaster); +} +void ai_Original_SetSearch(object oAssociate, int bTurnOn) +{ + if(GetRacialType(oAssociate) != RACIAL_TYPE_ELF) SetActionMode(oAssociate, ACTION_MODE_DETECT, bTurnOn); +} +void ai_Original_SetStealth(object oAssociate, int bTurnOn) +{ + SetActionMode(oAssociate, ACTION_MODE_STEALTH, bTurnOn); +} +void ai_Philos_Guard(object oMaster, object oCreature) +{ + ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_DEFEND_MASTER, TRUE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, FALSE); + ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE); + int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI); + ai_HighlightWidgetMode(oMaster, oCreature, nToken); + if(!ai_GetIsBusy(oCreature) && ai_GetIsInCombat(oCreature)) + { + object oLastAttacker = GetLastHostileActor(oMaster); + if(oLastAttacker != OBJECT_INVALID) ai_DoAssociateCombatRound(oCreature, oLastAttacker); + else AssignCommand(oCreature, ActionMoveToObject(oMaster, TRUE)); + } + ai_SendMessages(GetName(oCreature) + " is now guarding you!", AI_COLOR_YELLOW, oMaster); + aiSaveAssociateModesToDb(oMaster, oCreature); +} +void ai_Philos_Follow(object oMaster) +{ + object oCreature = OBJECT_SELF; + ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, TRUE); + ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE); + int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI); + ai_HighlightWidgetMode(oMaster, oCreature, nToken); + aiSaveAssociateModesToDb(oMaster, oCreature); + // To follow we probably should be running and not searching or hiding. + if(GetDetectMode(oCreature) && !GetHasFeat(FEAT_KEEN_SENSE, oCreature)) SetActionMode(oCreature, ACTION_MODE_DETECT, FALSE); + if(GetStealthMode(oCreature)) SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + ai_PassActionToAssociates(oCreature, ACTION_FOLLOW); + if(ai_IsInCombatRound(oCreature)) ai_ClearCombatState(oCreature); + ai_ClearCreatureActions(TRUE); + object oTarget = GetLocalObject(oCreature, AI_FOLLOW_TARGET); + if(oTarget == OBJECT_INVALID) oTarget = oMaster; + ActionMoveToObject(oTarget, TRUE, ai_GetFollowDistance(oCreature)); + ai_SendMessages(GetName(oCreature) + " is now following " + GetName(oTarget) + "!", AI_COLOR_YELLOW, oMaster); +} +void ai_Philos_StandGround(object oMaster) +{ + object oCreature = OBJECT_SELF; + ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, TRUE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_DEFEND_MASTER, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, FALSE); + ai_PassActionToAssociates(oCreature, ACTION_FOLLOW, FALSE); + ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE); + int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI); + ai_HighlightWidgetMode(oMaster, oCreature, nToken); + if(ai_IsInCombatRound(oCreature)) + { + ai_ClearCombatState(oCreature); + DeleteLocalObject(oCreature, AI_ATTACKED_PHYSICAL); + DeleteLocalObject(oCreature, AI_ATTACKED_SPELL); + } + ai_ClearCreatureActions(TRUE); + ai_SendMessages(GetName(oCreature) + " is now standing their ground!", AI_COLOR_YELLOW, oMaster); + aiSaveAssociateModesToDb(oMaster, oCreature); +} +void ai_Philos_AttackNearest(object oMaster, object oCreature) +{ + ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_DEFEND_MASTER, FALSE); + ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, FALSE); + ai_PassActionToAssociates(oCreature, ACTION_FOLLOW, FALSE); + ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE); + int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI); + ai_HighlightWidgetMode(oMaster, oCreature, nToken); + // Removes any targets the PC may have given the associate. + DeleteLocalObject(oCreature, AI_PC_LOCKED_TARGET); + // This resets a henchmens failed Moral save in combat. + string sScript = GetLocalString(oCreature, AI_COMBAT_SCRIPT); + if(sScript == "ai_coward") + { + sScript = GetLocalString(oCreature, AI_DEFAULT_SCRIPT); + SetLocalString(oCreature, AI_COMBAT_SCRIPT, sScript); + } + if(!ai_GetIsBusy(oCreature)) + { + object oEnemy = ai_GetNearestEnemy(oCreature, 1, 7, 7); + if(oEnemy != OBJECT_INVALID && GetDistanceBetween(oCreature, oEnemy) < AI_RANGE_BATTLEFIELD) + { + ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:"); + // If master is attacking a target we will attack them too! + if(!ai_GetIsInCombat(oCreature)) ai_StartAssociateCombat(oCreature); + object oTarget = ai_GetAttackedTarget(oMaster); + if(oTarget == OBJECT_INVALID) ai_DoAssociateCombatRound(oCreature); + else ai_DoAssociateCombatRound(oCreature, oTarget); + } + else + { + object oTarget = GetLocalObject(oCreature, AI_FOLLOW_TARGET); + if(oTarget == OBJECT_INVALID) oTarget = oMaster; + AssignCommand(oCreature, ActionMoveToObject(oMaster, TRUE, ai_GetFollowDistance(oCreature))); + } + } + ai_SendMessages(GetName(oCreature) + " is now in normal mode!", AI_COLOR_YELLOW, oMaster); + aiSaveAssociateModesToDb(oMaster, oCreature); +} +void ai_Philos_SetSearch(object oMaster, object oCreature, string sAssociateType, int bTurnOn) +{ + if(bTurnOn) + { + ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH, TRUE); + SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE); + ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, TRUE); + //ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, TRUE); + ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode On"); + } + else + { + ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH, FALSE); + SetActionMode(oCreature, ACTION_MODE_DETECT, FALSE); + ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, FALSE); + //ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, FALSE); + ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode Off"); + } + aiSaveAssociateModesToDb(oMaster, oCreature); +} +void ai_Philos_SetStealth(object oMaster, object oCreature, string sAssociateType, int bTurnOn) +{ + if(bTurnOn) + { + ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH); + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + ai_PassActionToAssociates(oCreature, ACTION_MODE_STEALTH, TRUE); + ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode On"); + } + else + { + ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH, FALSE); + SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + ai_PassActionToAssociates(oCreature, ACTION_MODE_STEALTH, FALSE); + //ai_PassActionToAssociates(oCreature, ACTION_MODE_STEALTH, FALSE); + ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode Off"); + } + aiSaveAssociateModesToDb(oMaster, oCreature); +} +void ai_DoCommand(object oPC, object oAssociate, int nCommand) +{ + int nIndex = 1; + if(oPC == oAssociate) + { + if(nCommand == 1) // Guard PC. + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Guard()); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Guard()); + } + } + // Use Philos AI commands. + else + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) ai_Philos_Guard(oPC, oAssociate); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) ai_Philos_Guard(oPC, oAssociate); + } + } + } + else if(nCommand == 2) // Follow PC. + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Follow()); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Follow()); + } + } + // Use Philos AI commands. + else + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_Follow(oPC)); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_Follow(oPC)); + } + } + } + else if(nCommand == 3) // Standground. + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_StandGround()); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_StandGround()); + } + } + // Use Philos AI commands. + else + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_StandGround(oPC)); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_StandGround(oPC)); + } + } + } + else if(nCommand == 4) // Normal mode - i.e. Attack nearest. + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_AttackNearest()); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_AttackNearest()); + } + } + // Use Philos AI commands. + else + { + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) ai_Philos_AttackNearest(oPC, oAssociate); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) ai_Philos_AttackNearest(oPC, oAssociate); + } + } + } + if(nCommand == 5) // All associates toggle search mode + { + int bTurnOn = !ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_SEARCH); + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + ai_Original_SetSearch(oPC, bTurnOn); + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) ai_Original_SetSearch(oAssociate, bTurnOn); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) ai_Original_SetSearch(oAssociate, bTurnOn); + } + } + else + { + ai_Philos_SetSearch(oPC, oPC, "pc", bTurnOn); + string sAssociateType; + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) + { + sAssociateType = ai_GetAssociateType(oPC, oAssociate); + ai_Philos_SetSearch(oPC, oAssociate, sAssociateType, bTurnOn); + } + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) + { + sAssociateType = ai_GetAssociateType(oPC, oAssociate); + ai_Philos_SetSearch(oPC, oAssociate, sAssociateType, bTurnOn); + } + } + } + if(bTurnOn) + { + ai_SendMessages("Everyone is now in search mode!", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_search_tooltip", " Everyone leave search mode"); + } + else + { + ai_SendMessages("Everyone has left search mode!", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_search_tooltip", " Everyone enter search mode"); + } + } + if(nCommand == 6) // All associate use stealth mode + { + int bTurnOn = !ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_STEALTH); + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + ai_Original_SetStealth(oPC, bTurnOn); + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) ai_Original_SetStealth(oAssociate, bTurnOn); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) ai_Original_SetStealth(oAssociate, bTurnOn); + } + } + else + { + ai_Philos_SetStealth(oPC, oPC, "pc", bTurnOn); + string sAssociateType; + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) + { + sAssociateType = ai_GetAssociateType(oPC, oAssociate); + ai_Philos_SetStealth(oPC, oAssociate, sAssociateType, bTurnOn); + } + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) + { + sAssociateType = ai_GetAssociateType(oPC, oAssociate); + ai_Philos_SetStealth(oPC, oAssociate, sAssociateType, bTurnOn); + } + } + } + if(bTurnOn) + { + ai_SendMessages("Everyone is now in stealth mode.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_stealth_tooltip", " Everyone leave stealth mode"); + } + else + { + ai_SendMessages("Everyone has left stealth mode.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_stealth_tooltip", " Everyone enter stealth mode"); + } + } + } + else + { + if(nCommand == 1) + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + AssignCommand(oAssociate, ai_Original_Guard()); + } + else ai_Philos_Guard(oPC, oAssociate); + } + else if(nCommand == 2) + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + AssignCommand(oAssociate, ai_Original_Follow()); + } + else AssignCommand(oAssociate, ai_Philos_Follow(oPC)); + } + else if(nCommand == 3) + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + AssignCommand(oAssociate, ai_Original_StandGround()); + } + else AssignCommand(oAssociate, ai_Philos_StandGround(oPC)); + } + else if(nCommand == 4) + { + // Not using Philos Henchman AI. Use vanilla commands. + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "") + { + AssignCommand(oAssociate, ai_Original_AttackNearest()); + } + else ai_Philos_AttackNearest(oPC, oAssociate); + } + } +} +void ai_Action(object oPC, object oAssociate) +{ + 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); + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION"); + ai_SendMessages("Select an action for " + GetName(oAssociate) + ".", AI_COLOR_YELLOW, oPC); + } + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_AIScript(object oPC, object oAssociate, string sAssociateType, int nToken) +{ + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) != "") + { + string sScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + string sIcon = "ir_scommand"; + if(sScript == "ai_a_ambusher") + { + sScript = "ai_a_flanker"; + sIcon = "ir_invite"; + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + ai_SendMessages(GetName(oAssociate) + " is now using flanking tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Flanker: Attacks enemies engaged with allies"); + } + else if(sScript == "ai_a_flanker") + { + sScript = "ai_a_peaceful"; + sIcon = "ir_ignore"; + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + ai_SendMessages(GetName(oAssociate) + " is now using peaceful tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Peaceful: Avoids attacking any enemies if possible"); + } + else if(sScript == "ai_a_peaceful") + { + sScript = "ai_a_defensive"; + sIcon = "ir_knockdwn"; + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + ai_SendMessages(GetName(oAssociate) + " is now using defensive tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Defensive: Attacks then uses Expertise/Parry"); + } + else if(sScript == "ai_a_defensive") + { + sScript = "ai_a_ranged"; + sIcon = "ir_ranger"; + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + ai_SendMessages(GetName(oAssociate) + " is now using ranged tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Ranged: Attacks from range as much as possible"); + } + else if(sScript == "ai_a_ranged") + { + sScript = "ai_a_cntrspell"; + sIcon = "ir_dcaster"; + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + ai_SendMessages(GetName(oAssociate) + " is now using counter spell tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Counter Spell: Tries to counter enemy spells"); + } + else if(sScript == "ai_a_cntrspell") + { + DeleteLocalString(oAssociate, AI_DEFAULT_SCRIPT); + ai_SetAssociateAIScript(oAssociate, FALSE); + sScript = GetLocalString(oAssociate, AI_DEFAULT_SCRIPT); + ai_SendMessages(GetName(oAssociate) + " is now using default tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Default tactics: Using the creatures base AI script"); + } + else + { + sScript = "ai_a_ambusher"; + sIcon = "ir_rogue"; + SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript); + ai_SendMessages(GetName(oAssociate) + " is now using ambush tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Ambusher: Attacks from a hidden position"); + } + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_image", JsonString(sIcon)); + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_label", JsonString("Tactics: " + sScript)); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + if(JsonGetType(JsonArrayGet(jAIData, 8)) == JSON_TYPE_NULL) jAIData = JsonArrayInsert(jAIData, JsonString(sScript)); + else jAIData = JsonArraySet(jAIData, 8, JsonString(sScript)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + else + { + if(GetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, oAssociate)) + { + SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, TRUE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate); + ai_SendMessages(GetName(oAssociate) + " is now using coward tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using coward tactics"); + } + else if(GetCombatCondition(X0_COMBAT_FLAG_COWARDLY, oAssociate)) + { + SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, TRUE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate); + ai_SendMessages(GetName(oAssociate) + " is now using defensive tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using defensive tactics"); + } + else if(GetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, oAssociate)) + { + SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_RANGED, TRUE, oAssociate); + ai_SendMessages(GetName(oAssociate) + " is now using ranged tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using ranged tactics"); + } + else if(GetCombatCondition(X0_COMBAT_FLAG_RANGED, oAssociate)) + { + SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate); + ai_SendMessages(GetName(oAssociate) + " is now using normal tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using ambush tactics"); + } + else + { + SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, TRUE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate); + SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate); + ai_SendMessages(GetName(oAssociate) + " is now using ambush tactics in combat.", AI_COLOR_YELLOW, oPC); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using ambush tactics"); + } + } +} +void ai_HavePCPlaceTrap(object oPC, object oAssociate) +{ + SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate); + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_GET_TRAP"); + ai_SendMessages(GetName(oAssociate) + " select a trap to place.", AI_COLOR_YELLOW, oPC); + OpenInventory(oAssociate, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ITEM, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_JumpAssociateToPC(object oPC) +{ + ai_ClearCreatureActions(TRUE); + JumpToObject(oPC); +} +void ai_JumpToPC(object oPC, object oAssociate) +{ + int nAssociateType, nHenchman, nHenchAssociate; + object oHenchman, oHenchmanAssociate; + if(oPC != oAssociate) + { + if(nAssociateType == ASSOCIATE_TYPE_HENCHMAN) + { + for(nHenchAssociate = 2; nHenchAssociate <= 5; nHenchAssociate++) + { + oHenchmanAssociate = GetAssociate(nHenchAssociate, oHenchman, 1); + if(oHenchmanAssociate != OBJECT_INVALID) + { + AssignCommand(oHenchmanAssociate, ai_JumpAssociateToPC(oPC)); + } + } + AssignCommand(oHenchman, ai_JumpAssociateToPC(oPC)); + } + else AssignCommand(oAssociate, ai_JumpAssociateToPC(oPC)); + return; + } + for(nAssociateType = 1; nAssociateType <= 5; nAssociateType++) + { + if(nAssociateType == ASSOCIATE_TYPE_HENCHMAN) + { + for(nHenchman = 1; nHenchman <= AI_MAX_HENCHMAN; nHenchman++) + { + oHenchman = GetAssociate(nAssociateType, oPC, nHenchman); + if(oHenchman != OBJECT_INVALID) + { + for(nHenchAssociate = 2; nHenchAssociate <= 5; nHenchAssociate++) + { + oHenchmanAssociate = GetAssociate(nHenchAssociate, oHenchman, 1); + if(oHenchmanAssociate != OBJECT_INVALID) + { + AssignCommand(oHenchmanAssociate, ai_JumpAssociateToPC(oPC)); + } + } + AssignCommand(oHenchman, ai_JumpAssociateToPC(oPC)); + } + } + } + else + { + oHenchman = GetAssociate(nAssociateType, oPC, 1); + if(oHenchman != OBJECT_INVALID) AssignCommand(oHenchman, ai_JumpAssociateToPC(oPC)); + } + } +} +void ai_GhostMode(object oPC, object oAssociate, int nToken, string sAssociateType) +{ + string sText; + if(ai_GetAIMode(oAssociate, AI_MODE_GHOST)) + { + ai_SetAIMode(oAssociate, AI_MODE_GHOST, FALSE); + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + sText = " Turn On clipping through creatures for " + GetName(oAssociate); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ghost_mode_tooltip", sText); + ai_SendMessages(GetName(oAssociate) + " is not in Ghost Mode and will run into creatures.", AI_COLOR_YELLOW, oPC); + } + else + { + ai_SetAIMode(oAssociate, AI_MODE_GHOST, TRUE); + effect eGhost = EffectCutsceneGhost(); + eGhost = UnyieldingEffect(eGhost); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oAssociate); + sText = " Turn Off clipping through creatures for " + GetName(oAssociate); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ghost_mode_tooltip", sText); + ai_SendMessages(GetName(oAssociate) + " is now in Ghost Mode and will clip through creatures.", AI_COLOR_YELLOW, oPC); + } +} +void ai_ChangeCameraView(object oPC, object oAssociate) +{ + object oCamAssociate = GetLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE"); + if(oCamAssociate == oAssociate) + { + DeleteLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE"); + AttachCamera(oPC, oPC); + } + else + { + SetLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE", oAssociate); + AttachCamera(oPC, oAssociate); + } +} +void ai_SelectCameraView(object oPC) +{ + SetLocalString(oPC, AI_TARGET_MODE, "DM_SELECT_CAMERA_VIEW"); + ai_SendMessages(GetName(oPC) + " select an object to change the camera view to.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_CREATE, MOUSECURSOR_NOCREATE); +} +void ai_OpenInventory(object oAssociate, object oPC) +{ + // Funny things happen when you open associate inventories when they are not + // within sight. + if(LineOfSightObject(oPC, oAssociate)) + { + OpenInventory(oAssociate, oPC); + } + else ai_SendMessages(GetName(oAssociate) + " is not within sight!", AI_COLOR_RED, oPC); +} +void ai_SelectOpenInventory(object oPC) +{ + SetLocalString(oPC, AI_TARGET_MODE, "DM_SELECT_OPEN_INVENTORY"); + ai_SendMessages(GetName(oPC) + " select an object to open its inventory.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); +} +void ai_Plugin_Execute(object oPC, string sElem, int bUser = 0) +{ + int nIndex = StringToInt(GetStringRight(sElem, 1)); + json jPlugins, jPlugin; + if(bUser == 1) // From DM command menu. + { + string sName = ai_RemoveIllegalCharacters(GetName(oPC)); + jPlugins = ai_GetCampaignDbJson("plugins", sName, AI_DM_TABLE); + } + else if(bUser == 2) // From DM plugin menu, master plugin list. + { + jPlugins = ai_GetCampaignDbJson("plugins"); + } + else jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + jPlugin = JsonArrayGet(jPlugins, nIndex); + string sScript = JsonGetString(JsonArrayGet(jPlugin, 0)); + if(ResManGetAliasFor(sScript, RESTYPE_NCS) == "") + { + ai_SendMessages(sScript + " not found by ResMan!", AI_COLOR_RED, oPC); + } + else + { + string sName = JsonGetString(JsonArrayGet(jPlugin, 2)); + ai_SendMessages("Executing plugin " + sName + ".", AI_COLOR_GREEN, oPC); + ExecuteScript(sScript, oPC); + } +} diff --git a/_module/nss/0i_color.nss b/_module/nss/0i_color.nss new file mode 100644 index 00000000..b18fe7ac --- /dev/null +++ b/_module/nss/0i_color.nss @@ -0,0 +1,70 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_color +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Include scripts that are used to change the color of names and text. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// Basic color codes. Message Notes +const string AI_COLOR_BLACK = "000"; // Nothing. +const string AI_COLOR_WHITE = "999"; // _Debug messages. +const string AI_COLOR_GRAY = "666"; // Server messages +const string AI_COLOR_YELLOW = "990"; // Generic messages to players. +const string AI_COLOR_DARK_YELLOW = "660"; // +const string AI_COLOR_RED = "900"; // Negative message to players. +const string AI_COLOR_DARK_RED = "600"; // +const string AI_COLOR_GREEN = "080"; // Positive message to players. +const string AI_COLOR_DARK_GREEN = "060"; // +const string AI_COLOR_BLUE = "009"; // +const string AI_COLOR_DARK_BLUE = "006"; // In game descriptive text. +const string AI_COLOR_CYAN = "099"; // +const string AI_COLOR_DARK_CYAN = "066"; // +const string AI_COLOR_MAGENTA = "909"; // +const string AI_COLOR_DARK_MAGENTA = "606";// +const string AI_COLOR_LIGHT_MAGENTA = "868"; // <âcâ> Combat text: Enemy name color. +const string AI_COLOR_ORANGE = "950"; // +const string AI_COLOR_DARK_ORANGE = "940"; // Combat text: base text color. +const string AI_COLOR_GOLD = "860"; // +// Strips the color codes from sText +string ai_StripColorCodes(string sText); +// This function will make sString be the specified color +// as specified in sRGB. RGB is the Red, Green, and Blue +// Each color can have a value from 0 to 9. +// 1 - 0(20)[ ] 142 - 5(8E)[?] +// 32 - 1(20)[ ] 170 - 6(AA)[ª] +// 57 - 2(39)[9] 198 - 7(C6)[Æ] +// 85 - 3(55)[U] 226 - 8(E2)[â] +// 113 - 4(71)[q] 255 - 9(FE)[ÿ] +string ai_AddColorToText(string sText, string sRGB = AI_COLOR_WHITE); + +string ai_StripColorCodes(string sText) +{ + string sColorCode, sChar; + int nStringLength = GetStringLength(sText); + int i = FindSubString(sText, "" + // End the color token + sText + ""; +} diff --git a/_module/nss/0i_combat.nss b/_module/nss/0i_combat.nss new file mode 100644 index 00000000..6c40eb1c --- /dev/null +++ b/_module/nss/0i_combat.nss @@ -0,0 +1,3498 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_combat +//////////////////////////////////////////////////////////////////////////////// + Include scripts for combat scripts. +*/////////////////////////////////////////////////////////////////////////////// +// Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// +#include "0i_messages" +#include "0i_items" +#include "0i_spells" +// This structure is used to represent the number and type of +// enemies that a creature is facing, divided into four main +// categories: FIGHTERS, CLERICS, MAGES, MONSTERS. +struct stClasses +{ + int FIGHTERS; + int FIGHTER_LEVELS; + int CLERICS; + int CLERIC_LEVELS; + int MAGES; + int MAGE_LEVELS; + int MONSTERS; + int MONSTER_LEVELS; + int TOTAL; + int TOTAL_LEVELS; +}; +struct stTarget +{ + object oTarget; + int nValue; + int nBestValue; + int nBestSecondaryValue; + float fNearestRange; + float fNearestSecondaryRange; + int nIndex; + int nSecondaryIndex; + string sTargetType; +}; +//****************************************************************************** +//************ 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 +// corresponding values for nCValue1 & nCValue2. +// NOTE: CREATURE_TYPE_PERCEPTION = 7, PERCEPTION_SEEN = 7. +// bDisabled = TRUE will also return any disabled targets that are not dead. +object ai_GetNearestEnemy(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1, int bDisabled = FALSE); +// Returns the nearest ally from oCreature. +// You may pass in any of the CREATURE_TYPE_* constants +// used in GetNearestCreature as nCType1 & nCType2, with +// corresponding values for nCValue1 & nCValue2. +// NOTE: CREATURE_TYPE_PERCEPTION = 7, PERCEPTION_SEEN = 7. +object ai_GetNearestAlly(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1); +// Returns the number of alive enemies grouped near oCreature within fDistance. +int ai_GetNumOfEnemiesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE); +// Returns the number of alive allies grouped near oCreature within fDistance. +int ai_GetNumOfAlliesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE); +// Returns the number of creatures of nRacial_Type within fDistance that can be seen by oCreature. +int ai_GetRacialTypeCount(object oCreature, int nRacial_Type, float fDistance = AI_RANGE_PERCEPTION); +// Returns the weakest attacker that is in melee or is attacking oCreature's master. +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); + +//****************************************************************************** +//*************** GET TARGETS USING COMBAT STATE FUNCTIONS ********************* +//****************************************************************************** +// These functions will find a target or an index to a target based on the +// combat state variables created by the function ai_SetCombatState. + +// 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); +// 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); +// 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); +// 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); +// 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); +// 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. +// 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. +// 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); +// 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 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. +// sTargetType is either AI_ENEMY or AI_ALLY. +int ai_GetMostWoundedIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the creature with the lowest health seen within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +object ai_GetMostWoundedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the index of the nearest ally with the least % of hitpoints within +// fMaxRange in the combat state. +// This also filters for AI_MODE_PARTY_HEALING_OFF and AI_MODE_SELF_HEALING_OFF. +// If no ally is found then it will return an index of 0. +int ai_GetAllyToHealIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION); +// Returns the ally with the lowest health seen within fMaxRange in the combat state. +// This also filters for AI_MODE_PARTY_HEALING_OFF and AI_MODE_SELF_HEALING_OFF. +// Returns OBJECT_INVALID if no creature is found. +object ai_GetAllyToHealTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION); +// Returns the creature with the lowest fortitude save seen within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +object ai_GetLowestFortitudeSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION); +// Returns the creature with the lowest reflex save seen within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +object ai_GetLowestReflexSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION); +// Returns the creature with the lowest will save seen within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +object ai_GetLowestWillSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION); +// Returns the creature with the lowest save based on nSpell save type seen +// within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +object ai_GetSpellTargetBasedOnSaves(object oCreature, int nSpell, float fMaxRange = AI_RANGE_PERCEPTION); +// 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_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. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetNearestClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the creature with the lowest combat rating 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. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetLowestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the creature with the highest combat rating 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. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetHighestCRClassTarget(object oCreature, int nClassType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the nearest creature seen of nRacialType within fMaxRange in the combat state. +// Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetNearestRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the creature with the lowest combat rating seen of nRacialType within +// fMaxRange in the combat state. Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetLowestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the creature with the highest combat rating seen of nRacialType within +// fMaxRange in the combat state. Returns OBJECT_INVALID if no creature is found. +// sTargetType is either AI_ENEMY or AI_ALLY. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetHighestCRRacialTarget(object oCreature, int nRacialType, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the nearest enemy seen that is attacking an ally with the least +// number of enemies on them within fMaxRange in the combat state. +// If none is found then it will return 0. +object ai_GetFlankTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE); +// Returns the nearest enemy creature seen wihtin fMaxRange that is a favored enemy +// of the caller in the combat state. +// Returns OBJECT_INVALID if no creature is found. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetNearestFavoredEnemyTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE); +// Returns the best target for melee combat based if we are in melee or not. +// If not in melee it will get the nearest target that is not in a dangerous +// area of effect for us to attack in the combat state. +// If in melee it will get the weakest target. +// If it returns OBJECT_INVALID then we should stop the attack. The only way +// to not get a target is if we have been told not to attack strong opponents. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetBestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE); +// Returns the nearest target for melee combat based if we are in melee or not. +// If not in melee it will get the nearest target that is not in a dangerous +// area of effect for us to attack in the combat state. +// If it returns OBJECT_INVALID then we should stop the attack. The only way +// to not get a target is if we have been told not to attack strong opponents. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetNearestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE); +// Returns the target with the lowest combat rating for melee combat based if +// we are in melee or not. If not in melee it will get the nearest target that +// is not in a dangerous area of effect for us to attack in the combat state. +// If it returns OBJECT_INVALID then we should stop the attack. The only way +// to not get a target is if we have been told not to attack strong opponents. +// bAlwaysAtk TRUE we attack everything! FALSE we don't attack strong enemies. +object ai_GetLowestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE); +// Returns the target with the highest combat rating for melee combat based if +// we are in melee or not. If not in melee it will get the nearest target that +// is not in a dangerous area of effect for us to attack in the combat state. +// If it returns OBJECT_INVALID then we should stop the attack. +object ai_GetHighestCRTargetForMeleeCombat(object oCreature, int nInMelee); +// 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_MonsterGetNearestIndex(object oMonster, float fMaxRange = AI_RANGE_PERCEPTION, string sTargetType = AI_ENEMY, int bAlwaysAtk = TRUE); +// Returns the index of the nearest enemy creature that can see oCreature. +int ai_GetNearestIndexThatSeesUs(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION); +// Returns the nearest creature attacking the caller within fMaxRange in the combat state. +// Returns OBJECT_INVALID if oCreature is not being attacked. +object ai_GetEnemyAttackingMe(object oCreature, float fMaxRange = AI_RANGE_MELEE); +// Returns the nearest creature attacking oAlly from oCreature within fMaxRange +// in the combat state. +// Returns OBJECT_INVALID if oAlly is not being attacked. +object ai_GetEnemyAttackingMyAlly(object oCreature, object oAlly, float fMaxRange = AI_RANGE_MELEE); +// Returns the number of enemies within fMaxRange of the caller in the combat state. +int ai_GetNumOfEnemiesInRange(object oCreature, float fMaxRange = AI_RANGE_MELEE); +// Returns the best ally target withing fMaxRange for nSpell to be cast on. +// Uses the ai_spells.2da file to pick a target. +object ai_GetAllyBuffTarget(object oCreature, int nSpell, float fMaxRange = AI_RANGE_BATTLEFIELD); + +//****************************************************************************** +//******************** OTHER COMBAT FUNCTIONS ******************************** +//****************************************************************************** + +// Returns the current round that oCreature is in for this combat. +int ai_GetCurrentRound(object oCreature); +// Returns the difficulty of the battle based on the combat state. +// nDifficulty is Enemy level - Ally level + 20 + Player adjustment. +// 20+ : Impossible - Cannot win. +// 17 to 19 : Overpowering - Use all of our powers. +// 15 to 16 : Very Difficult - Use all of our power (Highest level spells). +// 11 to 14 : Challenging - Use most of our power (Higher level powers). +// 8 to 10 : Moderate - Use half of our power (Mid level powers and less). +// 5 to 7 : Easy - Use our weaker powers (Lowest level powers). +// 2 to 4 : Effortless - Don't waste spells and powers on this. +// 1 or less: Pointless - We probably should ignore these dangers. +int ai_GetDifficulty(object oCreature); +// Returns oCreatures Combat rating. +//(BAB + AC - 10) / 2 +int ai_GetMyCombatRating(object oCreature); +// Returns the last creature oCreature attacked. +// bPhysical checks for creatures attacked in melee or range with a weapon. +// bSpell will look for creatures attacked by a spell. +object ai_GetAttackedTarget(object oCreature, int bPhysical = TRUE, int bSpell = FALSE); +// Returns TRUE if oCreature is of nClassType; +// May also check for general Class types with +// AI_CLASS_TYPE_ARCANE, AI_CLASS_TYPE_DIVINE, AI_CLASS_TYPE_CASTER, AI_CLASS_TYPE_WARRIOR. +int ai_CheckClassType(object oCreature, int nClassType); +// Returns TRUE if oCreature is of nRacialType; +// May also check for general racial types with +// AI_RACIAL_TYPE_ANIMAL_BEAST +int ai_CheckRacialType(object oCreature, int nRacialType); +// Saves oCreatures Normal appearance if they are not polymorphed and it has +// not already been saved. +void ai_SetNormalAppearance(object oCreature); +// Returns the normal appearance of oCreature. +int ai_GetNormalAppearance(object oCreature); +// Return the number and levels of all creatures within fMaxRange. +// They are grouped into Fighters, Clerics, Mages, and Monsters. +struct stClasses ai_GetFactionsClasses(object oCreature, int bEnemy = TRUE, float fMaxRange = AI_RANGE_BATTLEFIELD); +// This will return the class with the most levels. +// Returns a string of "FIGHTER", "CLERIC", "MAGE", or "MONSTER". +// Execute with GetFactionsClasses. +string ai_GetMostDangerousClass(struct stClasses stCount); +// Equips the best weapon, ranged or melee. +// Returns TRUE if equiped, FALSE if not. +// oTarget is the creature the caller is targeting. +void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID); +// Equips a melee weapon AND checks for shield, two weapons, two handed, etc. +// Returns TRUE if equiped, FALSE if not. +// oTarget is the creature the caller is targeting. +int ai_EquipBestMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID); +// Equips a ranged weapon AND checks for ammo. +// Returns TRUE if equiped, FALSE if not. +// oTarget is the creature the caller is targeting. +int ai_EquipBestRangedWeapon(object oCreature, object oTarget = OBJECT_INVALID); +// Equips the best weapon for a monk character. +// Returns TRUE if equiped, FALSE if not. +// oTarget is the creature the caller is targeting. +int ai_EquipBestMonkMeleeWeapon(object oCreature, object oTarget = OBJECT_INVALID); +// Returns TRUE if oCreature is in a Dangerous Area of Effect in fMaxRange. +// bMove will attempt to move oCreature out of the Dangerous AOE if needed. +int ai_IsInADangerousAOE(object oCreature, float fMaxRange = AI_RANGE_BATTLEFIELD, int bMove = FALSE); +// Returns 1 if oHidden has an Invisiblity effect, Can't be spotted but can be heard. +// Returns 2 if oHidden has a Darkness effect. Can't be spotted but can be heard. +// Returns 3 if oHidden has a Sanctuary effect, Can't be spotted or heard. +// Returns 4 if oHidden is in stealth mode, Can be spotted and heard. +int ai_GetIsHidden(object oHidden); +// Returns TRUE if if oCaster has a good chance of effecting oCreature with nSpell. +int ai_CastOffensiveSpellVsTarget(object oCaster, object oCreature, int nSpell); +// Gets the base DC for a dragon. +int ai_GetDragonDC(object oCreature); +// Set oCreature's ai scripts based on its first class or the variable "AI_DEFAULT_SCRIPT". +void ai_SetCreatureAIScript(object oCreature); +// Returns TRUE if oTarget is immune to sneak attacks. +int ai_IsImmuneToSneakAttacks(object oCreature, object oTarget); +// Returns TRUE if iIndex target has a higher combat rating than oCreature. +int ai_IsStrongerThanMe(object oCreature, int nIndex); +// Returns TRUE if oTarget's CR is within nAdj of oCreature's level, otherwise FALSE. +int ai_StrongOpponent(object oCreature, object oTarget, int nAdj = 2); +// Returns TRUE if attacking oTarget with Power attack is a good option. +int ai_PowerAttackGood(object oCreature, object oTarget, float fAdj); +// Returns TRUE if oTarget's AC - oCreature Atk - nAtkAdj can hit within 25% to 75%. +int ai_AttackPenaltyOk(object oCreature, object oTarget, float fAtkAdj); +// Returns TRUE if oCreature AC - oTarget's Atk is less than 20. +int ai_ACAdjustmentGood(object oCreature, object oTarget, float fACAdj); +// Checks oCreatures melee weapon to see if they can kill oTarget in one hit. +int ai_WillKillInOneHit(object oCreature, object oTarget); +// Returns TRUE if oCreature has Mobility, SpringAttack, or a high Tumble. +int ai_CanIMoveInCombat(object oCreature); +// Returns TRUE if oCreature can safely fire a ranged weapon. +int ai_CanIUseRangedWeapon(object oCreature, int nInMelee); +// Returns TRUE if oCreature moves before the action. FALSE if they do not move. +// 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); + +//****************************************************************************** +//************ GET TARGETS USING THE OBJECT SEARCH FUNCTIONS ******************* +//****************************************************************************** +object ai_GetNearestEnemy(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1, int bDisabled = FALSE) +{ + object oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + oCreature, nNth, nCType1, nCValue1, nCType2, nCValue2); + if(bDisabled) + { + while(oTarget != OBJECT_INVALID && GetIsDead(oTarget)) + { + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + oCreature, ++nNth, nCType1, nCValue1, nCType2, nCValue2); + } + } + else + { + while(oTarget != OBJECT_INVALID && ai_Disabled(oTarget)) + { + oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, + oCreature, ++nNth, nCType1, nCValue1, nCType2, nCValue2); + } + } + return oTarget; +} +object ai_GetNearestAlly(object oCreature, int nNth = 1, int nCType1 = -1, int nCValue1 = -1, int nCType2 = -1, int nCValue2 = -1) +{ + return GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, + oCreature, ++nNth, nCType1, nCValue1, nCType2, nCValue2); +} +int ai_GetNumOfEnemiesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE) +{ + int nCnt; + location lLocation = GetLocation(oCreature); + object oEnemy = GetFirstObjectInShape(SHAPE_SPHERE, fDistance, lLocation); + while(oEnemy != OBJECT_INVALID) + { + if(GetIsEnemy(oEnemy, oCreature) && !GetIsDead(oEnemy)) nCnt++; + oEnemy = GetNextObjectInShape(SHAPE_SPHERE, fDistance, lLocation); + } + return nCnt; +} +int ai_GetNumOfAlliesInGroup(object oCreature, float fDistance = AI_RANGE_MELEE) +{ + int nCnt; + location lLocation = GetLocation(oCreature); + object oAlly = GetFirstObjectInShape(SHAPE_SPHERE, fDistance, lLocation); + while(oAlly != OBJECT_INVALID) + { + if(GetReputation(oCreature, oAlly) > 89 && oAlly != oCreature && !GetIsDead(oAlly)) + { + nCnt++; + } + oAlly = GetNextObjectInShape(SHAPE_SPHERE, fDistance, lLocation); + } + return nCnt; +} +int ai_GetRacialTypeCount(object oCreature, int nRacial_Type, float fDistance = AI_RANGE_PERCEPTION) +{ + int nCnt = 1; + int nCount = 0; + object oEnemy = ai_GetNearestEnemy(oCreature, nCnt, + CREATURE_TYPE_PERCEPTION, + PERCEPTION_SEEN, + CREATURE_TYPE_RACIAL_TYPE, + nRacial_Type); + while(oEnemy != OBJECT_INVALID && GetDistanceBetween(oEnemy, oCreature) <= fDistance) + { + if(!ai_GetHasEffectType(oEnemy, EFFECT_TYPE_TURNED)) nCount++; + nCnt++; + oEnemy = ai_GetNearestEnemy(oCreature, nCnt, + CREATURE_TYPE_PERCEPTION, + PERCEPTION_SEEN, + CREATURE_TYPE_RACIAL_TYPE, + nRacial_Type); + } + return nCount; +} +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)); + int nEnemyCombatRating, nWeakestCombatRating, nCntr = 1; + float fNearest = AI_RANGE_MELEE + 1.0f; + // Get the weakest opponent in melee with our master. + object oEnemy = ai_GetNearestEnemy(oMaster, nCntr, 7, 7); + float fDistance = GetDistanceBetween(oMaster, oEnemy); + 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 (nEnemyCombatRating < nWeakestCombatRating || + nEnemyCombatRating == nWeakestCombatRating && fDistance < fNearest) + { + fNearest = fDistance; + nWeakestCombatRating = nEnemyCombatRating; + oTarget = oEnemy; + } + 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); + return oTarget; +} + +//****************************************************************************** +//******************** SET/CLEAR COMBAT STATE FUNCTIONS ************************ +//****************************************************************************** + +object ai_SetCombatState(object oCreature) +{ + if(AI_DEBUG) ai_Counter_Start(); + object oMaster = GetMaster(); + if(oMaster == OBJECT_INVALID) oMaster = oCreature; + int nEnemyNum, nEnemyPower, nAllyNum, nAllyPower, nInMelee, nMagic; + int nHealth, nNth, nAllies, nPower, nDisabled, bThreat,nObjects; + int nEnemyHighestPower, nAllyHighestPower; + float fNearest = AI_RANGE_BATTLEFIELD; + float fDistance; + float fMaxRange = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); + if(fMaxRange == 0.0) fMaxRange = AI_RANGE_PERCEPTION; + string sCnt, sDebugText; + location lLocation = GetLocation(oMaster); + object oMelee, oNearestEnemy = OBJECT_INVALID; + if(AI_DEBUG) ai_Debug("0i_combat", "491", "************************************************************"); + if(AI_DEBUG) ai_Debug("0i_combat", "492", "******************* CREATING COMBAT DATA *******************"); + if(AI_DEBUG) ai_Debug("0i_combat", "493", GetName(oCreature)); + // We want to include ourselves in the combat state. + object oObject = GetFirstObjectInShape(SHAPE_SPHERE, AI_RANGE_BATTLEFIELD, lLocation); + // Get all creatures within 40 meters(5 meters beyond our perception of 35). + // Centered on either the creature or their master. + while(oObject != OBJECT_INVALID) + { + // Process all enemies. + if(GetIsEnemy(oObject, oCreature)) + { + if(GetObjectSeen(oObject, oCreature) || GetObjectHeard(oObject, oCreature)) + { + fDistance = GetDistanceBetween(oObject, oCreature); + if(fDistance <= fMaxRange) + { + // ********** Get the Total levels of the Enemy ********** + nPower = ai_GetCharacterLevels(oObject); + if(nPower < 1) nPower = 1; + if(nEnemyHighestPower < nPower) nEnemyHighestPower = nPower; + nEnemyPower += nPower; + // ********** Check if the Enemy is disabled ********** + bThreat = TRUE; + nDisabled = ai_Disabled(oObject); + if(nDisabled) + { + if(AI_DEBUG) sDebugText += "**** DISABLED(" + IntToString(nDisabled) + ") ****"; + // Decide if they are still a threat: 1 - dead, 2 - Bleeding. + if(nDisabled == 1 || nDisabled == 2 || + //nDisabled == EFFECT_TYPE_CONFUSED || + //nDisabled == EFFECT_TYPE_FRIGHTENED || + //nDisabled == EFFECT_TYPE_PARALYZE || + nDisabled == EFFECT_TYPE_CHARMED || + nDisabled == EFFECT_TYPE_PETRIFY) + { + bThreat = FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "527", "Enemy: " + GetName(oObject) + sDebugText); + } + } + // If they are using the coward ai then treat them as frightened. + // we place it here as an else so we don't overwrite another disabled effect. + else if(GetLocalString(oObject, AI_COMBAT_SCRIPT) == "ai_coward") + { + nDisabled = EFFECT_TYPE_FRIGHTENED; + // !!!! For /DEBUG CODE !!!! + if(AI_DEBUG) sDebugText += "**** DISABLED(" + IntToString(nDisabled) + ") ****"; + } + if(bThreat) + { + sCnt = IntToString(++nEnemyNum); + // ********** Set if the Enemy is disabled ********** + SetLocalInt(oCreature, AI_ENEMY_DISABLED + sCnt, nDisabled); + // ********** Set the Enemy Object ********** + SetLocalObject(oCreature, AI_ENEMY + sCnt, oObject); + // ********** Set the Enemy Combat Rating ********** + SetLocalInt(oCreature, AI_ENEMY_COMBAT + sCnt, ai_GetMyCombatRating(oObject)); + // ********** Set the Enemy Health Percentage ********** + nHealth = ai_GetPercHPLoss(oObject); + SetLocalInt(oCreature, AI_ENEMY_HEALTH + sCnt, nHealth); + // ********** Set the number of enemies near the enemy ********** + nInMelee = 0; + nNth = 1; + oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, nNth); + while(oMelee != OBJECT_INVALID && !GetIsDead(oMelee) && + GetDistanceBetween(oMelee, oObject) < AI_RANGE_MELEE) + { + // We add an enemy to the group. + if(GetIsEnemy(oMelee, oCreature)) nInMelee++; + oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, ++nNth); + } + SetLocalInt(oCreature, AI_ENEMY_MELEE + sCnt, nInMelee); + // ********** Set the Enemies distance ********** + fDistance = GetDistanceBetween(oObject, oCreature); + SetLocalFloat(oCreature, AI_ENEMY_RANGE + sCnt, fDistance); + // ********** Set if the Enemy is perceived ********** + if(GetObjectSeen(oObject, oCreature) || + (GetObjectHeard(oObject, oCreature) && fDistance <= AI_RANGE_MELEE && + ai_GetIsHidden(oObject))) + { + SetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt, TRUE); + if(AI_DEBUG) sDebugText += "**** PERCEIVED Seen: " + + IntToString(GetObjectSeen(oObject, oCreature)) + + " Heard: " + IntToString(GetObjectHeard(oObject, oCreature)) + " ****"; + } + else SetLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt, FALSE); + // ********** Set the Nearest Enemy seen ********** + if(fDistance < fNearest) + { + fNearest = fDistance; + oNearestEnemy = oObject; + } + } + } + // !!! Debug code !!! + if(AI_DEBUG && fDistance < AI_RANGE_MELEE) sDebugText += "**** MELEE ****"; + if(AI_DEBUG) ai_Debug("0i_combat", "587", "Enemy(" + IntToString(nEnemyNum) + "): " + + GetName(oObject) + sDebugText); + if(AI_DEBUG) ai_Debug("0i_combat", "589", "nHealth: " + IntToString(nHealth) + + " nInMelee: " + IntToString(nInMelee) + + " fDistance: " + FloatToString(fDistance, 0, 2) + + " nNum: " + IntToString(nEnemyNum) + + " nPower: " + IntToString(nEnemyPower / 2)); + } + else + { + // ********** Also add the levels of Unknown Enemies *********** + nPower = FloatToInt(ai_GetCharacterLevels(oObject) / 1.5); + if(nPower < 1) nPower = 1; + nEnemyPower += nPower; + if(AI_DEBUG) ai_Debug("0i_combat", "601", "Enemy(NOT PERCEIVED): " + + GetName(oObject) + " fDistance: " + + FloatToString(GetDistanceBetween(oObject, oCreature), 0, 2) + + " nPower: " + IntToString(nEnemyPower)); + } + } + // Process all Allies. + else if(GetFactionEqual(oObject, oCreature)) + { + // ********** Set if the Ally is disabled ********** + nDisabled = ai_Disabled(oObject); + if(nDisabled) + { + sDebugText += "**** DISABLED(" + IntToString(nDisabled) + ") ****"; + SetLocalInt(oCreature, AI_ALLY_DISABLED + sCnt, nDisabled); + } + if(nDisabled != 1) + { + sCnt = IntToString(++nAllyNum); + // ********** Set the Ally Object ********** + SetLocalObject(oCreature, AI_ALLY + sCnt, oObject); + // ********** Set the Ally Combat Rating ********** + SetLocalInt(oCreature, AI_ALLY_COMBAT + sCnt, ai_GetMyCombatRating(oObject)); + // ********** Set the Ally Health Percentage ********** + nHealth = ai_GetPercHPLoss(oObject); + SetLocalInt(oCreature, AI_ALLY_HEALTH + sCnt, nHealth); + // ********** Set the number of enemies near the ally ********** + nInMelee = 0; + nNth = 1; + oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, nNth); + while(oMelee != OBJECT_INVALID && !GetIsDead(oMelee) && + GetDistanceBetween(oMelee, oObject) < AI_RANGE_MELEE) + { + if(GetIsEnemy(oMelee, oCreature)) nInMelee++; + //else nInMelee--; + oMelee = GetNearestObject(OBJECT_TYPE_CREATURE, oObject, ++nNth); + } + SetLocalInt(oCreature, AI_ALLY_MELEE + sCnt, nInMelee); + // ********** Set the Allies distance ********** + SetLocalFloat(oCreature, AI_ALLY_RANGE + sCnt, GetDistanceBetween(oObject, oCreature)); + // ********** All allies are considered to be seen ********** + SetLocalInt(oCreature, AI_ALLY_PERCEIVED + sCnt, TRUE); + // ********** Get the Total levels of the Allies ********** + nPower = ai_GetCharacterLevels(oObject); + if(nAllyHighestPower < nPower) nAllyHighestPower = nPower; + nAllyPower +=(nPower * nHealth) / 100; + if(AI_DEBUG) ai_Debug("0i_combat", "647", "Ally(" + IntToString(nAllyNum) + "): " + + GetName(oObject) + sDebugText); + if(AI_DEBUG) ai_Debug("0i_combat", "649", "nHealth: " + IntToString(nHealth) + + " nInMelee: " + IntToString(nInMelee) + + " fDistance: " + FloatToString(GetDistanceToObject(oObject), 0, 2) + + " nNum: " + IntToString(nAllyNum) + + " nPower: " + IntToString(nAllyPower / 2)); + } + } + if(AI_DEBUG) sDebugText = ""; + oObject = GetNextObjectInShape(SHAPE_SPHERE, AI_RANGE_BATTLEFIELD, lLocation); + } + if(AI_DEBUG) ai_Debug("0i_combat", "659", "Nearest Enemy: " + GetName(oNearestEnemy)); + if(AI_DEBUG) ai_Debug("0i_combat", "660", "****************** FINISHED COMBAT DATA *******************"); + if(AI_DEBUG) ai_Debug("0i_combat", "661", "************************************************************"); + // Lets save processing by only clearing previous enemy data we don't overwrite. + int nPreviousEnd = GetLocalInt(oCreature, AI_ENEMY_NUMBERS); + int nCnt = nEnemyNum + 1; + if(AI_DEBUG) ai_Debug("0i_combat", "665", "Clearing Enemy Combat Data: nPreviousEnd: " + + IntToString(nPreviousEnd) + " nCurrentEnd: " + IntToString(nCnt - 1)); + while(nPreviousEnd >= nCnt) + { + sCnt = IntToString(nCnt); + if(AI_DEBUG) ai_Debug("0i_combat", "670", "Clearing Enemy Combat Data: " + sCnt + " " + + GetName(GetLocalObject(oCreature, AI_ENEMY + sCnt))); + DeleteLocalObject(oCreature, AI_ENEMY + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt); + DeleteLocalFloat(oCreature, AI_ENEMY_RANGE + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_COMBAT + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_MELEE + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_HEALTH + sCnt); + nCnt ++; + } + // Lets save processing by only clearing previous ally data we don't overwrite. + nPreviousEnd = GetLocalInt(oCreature, AI_ALLY_NUMBERS); + nCnt = nAllyNum + 1; + if(AI_DEBUG) ai_Debug("0i_combat", "683", "Clearing Ally Combat Data: nPreviousEnd: " + + IntToString(nPreviousEnd) + " nCurrentEnd: " + IntToString(nCnt - 1)); + while(nPreviousEnd >= nCnt) + { + sCnt = IntToString(nCnt); + if(AI_DEBUG) ai_Debug("0i_combat", "688", "Clearing Ally Combat Data: " + sCnt + " " + + GetName(GetLocalObject(oCreature, AI_ENEMY + sCnt))); + DeleteLocalObject(oCreature, AI_ALLY + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_PERCEIVED + sCnt); + DeleteLocalFloat(oCreature, AI_ALLY_RANGE + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_COMBAT + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_MELEE + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_HEALTH + sCnt); + nCnt ++; + } + // Finally set all group states. + SetLocalInt(oCreature, AI_ENEMY_NUMBERS, nEnemyNum); + // Total enemy power is half the levels of all enemies + the total levels + // of the highest level enemy. + nEnemyPower = (nEnemyPower / 2) + nEnemyHighestPower; + SetLocalInt(oCreature, AI_ENEMY_POWER, nEnemyPower); + SetLocalObject(oCreature, AI_ENEMY_NEAREST, oNearestEnemy); + SetLocalInt(oCreature, AI_ALLY_NUMBERS, nAllyNum); + // Total ally power is half the levels of all allies + the total levels + // of the highest level ally, only used by associates. + nAllyPower = (nAllyPower / 2) + nAllyHighestPower; + SetLocalInt(oCreature, AI_ALLY_POWER, nAllyPower); + if(AI_DEBUG) ai_Debug("0i_combat", "710", "nEnemyPower: " + IntToString(nEnemyPower) + + " nEnemyHighestPower: " + IntToString(nEnemyHighestPower) + + " nAllyPower: " + IntToString(nAllyPower) + + " nAllyHighestPower: " + IntToString(nAllyHighestPower)); + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + " has finished the Combat State"); + return oNearestEnemy; +} +void ai_ClearCombatState(object oCreature) +{ + int bEnemyDone, bAllyDone, nCnt = 1; + int nEnemyNum = GetLocalInt(oCreature, AI_ENEMY_NUMBERS); + int nAllyNum = GetLocalInt(oCreature, AI_ALLY_NUMBERS); + if(AI_DEBUG) ai_Debug("0i_combat", "722", "Clearing " + GetName(oCreature) + "'s combat state." + + " nEnemyNum: " + IntToString(nEnemyNum) + " nAllyNum: " + IntToString(nAllyNum)); + string sCnt; + while(!bEnemyDone || !bAllyDone) + { + sCnt = IntToString(nCnt); + if(nCnt <= nEnemyNum) + { + if(AI_DEBUG) ai_Debug("0i_combat", "730", "Clearing " + GetName(GetLocalObject(oCreature, AI_ENEMY + sCnt)) + "."); + DeleteLocalObject(oCreature, AI_ENEMY + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_DISABLED + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_PERCEIVED + sCnt); + DeleteLocalFloat(oCreature, AI_ENEMY_RANGE + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_COMBAT + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_MELEE + sCnt); + DeleteLocalInt(oCreature, AI_ENEMY_HEALTH + sCnt); + } + else bEnemyDone = TRUE; + if(nCnt <= nAllyNum) + { + if(AI_DEBUG) ai_Debug("0i_combat", "742", "Clearing " + GetName(GetLocalObject(oCreature, AI_ALLY + sCnt)) + "."); + DeleteLocalObject(oCreature, AI_ALLY + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_DISABLED + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_PERCEIVED + sCnt); + DeleteLocalFloat(oCreature, AI_ALLY_RANGE + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_COMBAT + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_MELEE + sCnt); + DeleteLocalInt(oCreature, AI_ALLY_HEALTH + sCnt); + } + else bAllyDone = TRUE; + nCnt++; + } + DeleteLocalObject(oCreature, AI_ENEMY_NEAREST); + DeleteLocalInt(oCreature, AI_ENEMY_NUMBERS); + DeleteLocalInt(oCreature, AI_ENEMY_POWER); + DeleteLocalInt(oCreature, AI_ALLY_NUMBERS); + DeleteLocalObject(oCreature, AI_ALLY_POWER); + // Also clear these combat variables at the end of combat. + DeleteLocalObject(oCreature, AI_ATTACKED_PHYSICAL); + DeleteLocalObject(oCreature, AI_ATTACKED_SPELL); + // 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); + DeleteLocalInt(oCreature, AI_AM_I_SEARCHING); + DeleteLocalInt(oCreature, AI_TRIED_TO_HIDE); + DeleteLocalObject(oCreature, AI_IS_INVISIBLE); + DeleteLocalInt(oCreature, sLastActionVarname); + DeleteLocalInt(oCreature, AI_TALENTS_SET); + DeleteLocalInt(oCreature, AI_ROUND); + DeleteLocalInt(oCreature, sIPHasHasteVarname); + DeleteLocalInt(oCreature, sIPImmuneVarname); + DeleteLocalInt(oCreature, sIPResistVarname); + DeleteLocalInt(oCreature, sIPReducedVarname); + ai_EndCombatRound(oCreature); +} +//****************************************************************************** +//*********************** GET TARGETS INTERNAL FUNCTIONS *********************** +//****************************************************************************** +// These functions are used by the Get Index/ Get Target functions below. + +int ai_TargetIsInRangeofCreature(object oCreature, string sTargetType, string sCounter, float fMaxRange) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "796", "fMaxRange: " + FloatToString(fMaxRange, 0, 2) + + " fTargetRange: " + FloatToString(GetLocalFloat(oCreature, sTargetType + "_RANGE" + sCounter), 0, 2)); + return fMaxRange >= GetLocalFloat(oCreature, sTargetType + "_RANGE" + sCounter); +} +int ai_TargetIsInRangeofMaster(object oCreature, object oTarget) +{ + object oMaster = GetMaster(); + if(oMaster == OBJECT_INVALID) return TRUE; + 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) + + " 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 + + " fRange: " + FloatToString(GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex), 0, 2) + + " fNearestRange: " + FloatToString(sTarget.fNearestRange, 0, 2) + + " fNearestSecondaryRange: " + FloatToString(sTarget.fNearestSecondaryRange, 0, 2)); + // Lets put any disabled targets and associates if set in a secondary group. + if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) || + (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_CheckForLowestValueTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "835", "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) + + " sTarget.nValue: " + IntToString(sTarget.nValue) + + " sTarget.nBestValue: " + IntToString(sTarget.nBestValue) + + " sTarget.nBestSecondaryValue: " + IntToString(sTarget.nBestSecondaryValue)); + // Lets put any disabled targets and associates if set in a secondary group. + if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) || + (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_CheckForHighestValueTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "865", "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) + + " sTarget.nValue: " + IntToString(sTarget.nValue) + + " sTarget.nBestValue: " + IntToString(sTarget.nBestValue) + + " sTarget.nBestSecondaryValue: " + IntToString(sTarget.nBestSecondaryValue)); + // Lets put any disabled targets and associates if set in a secondary group. + if(GetLocalInt(oCreature, sTarget.sTargetType + "_DISABLED" + sIndex) || + (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_CheckForNearestAllTarget(object oCreature, struct stTarget sTarget, int nIndex, string sIndex) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "895", "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)) + + " 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) + { + sTarget.fNearestRange = GetLocalFloat(oCreature, sTarget.sTargetType + "_RANGE" + sIndex); + sTarget.nIndex = nIndex; + } + return sTarget; +} +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 + + " 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)) + + " Not an Associate? " + IntToString(GetAssociateType(sTarget.oTarget) < 2)); + if((!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) || GetAssociateType(sTarget.oTarget) < 2) && + 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) +{ + if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) + { + return ai_GetLowestCRIndex(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", "911", "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) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget = ai_CheckForNearestTarget(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", "931", "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) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "936", "Getting the nearest target."); + string sIndex = IntToString(ai_GetNearestIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetLowestCRIndex(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", "953", "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) && + 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", "974", "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) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "979", "Getting the lowest CR target."); + string sIndex = IntToString(ai_GetLowestCRIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetHighestCRIndex(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", "995", "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) && + 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", "1016", "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) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "1021", "Getting the highest CR target."); + string sIndex = IntToString(ai_GetHighestCRIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetLowestMeleeIndex(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", "1037", "Getting the lowest InMelee 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_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_MELEE" + 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", "1058", "Found lowest InMelee [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +int ai_GetHighestMeleeIndex(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", "1073", "Getting the highest InMelee 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_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", "1094", "Found highest InMelee [" + sTargetType + "] Index: " + IntToString(sTarget.nIndex)); + return sTarget.nIndex; +} +object ai_CheckForGroupedTarget(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)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetMostWoundedIndex(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 = 200; + sTarget.nBestSecondaryValue = 200; + 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", "1113", "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((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, sTargetType + "_HEALTH" + 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", "1130", "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."); + string sIndex = IntToString(ai_GetMostWoundedIndex(oCreature, fMaxRange, sTargetType, bAlwaysAtk)); + return GetLocalObject(oCreature, sTargetType + sIndex); +} +int ai_GetAllyToHealIndex(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.nBestValue = 200; + sTarget.sTargetType = AI_ALLY; + int nCounter = 1; + string sCounter = "1"; + 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: " + + sCounter + " " + GetName(sTarget.oTarget) + + " Seen: " + IntToString(GetLocalInt(oCreature, AI_ALLY_PERCEIVED + sCounter)) + + " GetIsDead: " + IntToString(GetIsDead(sTarget.oTarget))); + if(GetLocalInt(oCreature, AI_ALLY_PERCEIVED + sCounter) && + !GetIsDead(sTarget.oTarget)) + { + if(ai_TargetIsInRangeofCreature(oCreature, AI_ALLY, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetLocalInt(oCreature, AI_ALLY_HEALTH + sCounter); + sTarget = ai_CheckForLowestValueAllTarget(oCreature, sTarget, nCounter, sCounter); + } + } + sCounter = IntToString(++nCounter); + 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)); + 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."); + string sIndex = IntToString(ai_GetAllyToHealIndex(oCreature, fMaxRange)); + return GetLocalObject(oCreature, AI_ALLY + sIndex); +} +object ai_GetLowestFortitudeSaveTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = 200; + sTarget.nBestSecondaryValue = 200; + 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", "1113", "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) + + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetFortitudeSavingThrow(sTarget.oTarget); + sTarget = ai_CheckForLowestValueTarget(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", "1232", "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) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = 200; + sTarget.nBestSecondaryValue = 200; + 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", "1248", "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) + + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetReflexSavingThrow(sTarget.oTarget); + sTarget = ai_CheckForLowestValueTarget(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", "1269", "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) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = 200; + sTarget.nBestSecondaryValue = 200; + 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", "1285", "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) + + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget.nValue = GetWillSavingThrow(sTarget.oTarget); + sTarget = ai_CheckForLowestValueTarget(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", "1306", "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) +{ + // Check the spells save type in "ai_spells.2da" and find the weakest + // creature based on that save. + string sSaveType = Get2DAString("ai_spells", "SaveType", nSpell); + if(sSaveType == "Reflex") return ai_GetLowestReflexSaveTarget(oCreature, fMaxRange); + if(sSaveType == "Fortitude") return ai_GetLowestFortitudeSaveTarget(oCreature, fMaxRange); + if(sSaveType == "Will") return ai_GetLowestWillSaveTarget(oCreature, fMaxRange); + // If there is no save then lets see if we can find an enemy with the lowest health. + return ai_GetMostWoundedTarget(oCreature, fMaxRange); +} +int ai_GetNearestIndexThatSeesUs(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION) +{ + 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", "1334", "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) + + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "1373", GetName(sTarget.oTarget) + " can see us? " + + IntToString(GetObjectSeen(oCreature, sTarget.oTarget))); + if(GetObjectSeen(oCreature, sTarget.oTarget)) + { + sTarget = ai_CheckForNearestAllTarget(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(AI_DEBUG) ai_Debug("0i_combat", "1354", "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) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.sTargetType = AI_ENEMY; + int nCounter = 1; + string sCounter = "1"; + object oAttacking; + 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: " + + 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_IsImmuneToSneakAttacks(oCreature, sTarget.oTarget)) + { + if(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) + + " is attacking " + GetName(oAttacking)); + // They are attacking someone besides us or we are hidden? + if((oAttacking != OBJECT_INVALID && oAttacking != oCreature) || + GetActionMode(oCreature, ACTION_MODE_STEALTH)) + { + 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", "1398", "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)) + { + ai_GetLowestCRClassTarget(oCreature, nClassType, 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", "1591", "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((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget = ai_CheckForNearestTarget(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", "1611", "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) +{ + 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", "1626", "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((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", "1648", "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) +{ + 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", "1664", "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((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", "1685", "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) +{ + if(GetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY)) + { + ai_GetLowestCRRacialTarget(oCreature, nRacialType, 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", "1703", "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((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + ai_TargetIsInRangeofCreature(oCreature, sTargetType, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + sTarget = ai_CheckForNearestTarget(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", "1723", "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) +{ + 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", "1739", "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((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", "1760", "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) +{ + 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", "1776", "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((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", "1797", "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) +{ + struct stTarget sTarget; + sTarget.fNearestRange = fMaxRange + 1.0; + sTarget.fNearestSecondaryRange = sTarget.fNearestRange; + sTarget.nBestValue = -100; + sTarget.nBestSecondaryValue = -100; + sTarget.sTargetType = AI_ENEMY; + int nCounter = 1; + string sCounter = "1"; + int nRace, nRacialType; + while(nRace < 24) + { + // Find which favored enemies we have. + if(nRace < 1 && GetHasFeat(FEAT_FAVORED_ENEMY_ABERRATION, oCreature)) + { + nRace = 1; + nRacialType = RACIAL_TYPE_ABERRATION; + } + else if(nRace < 2 && GetHasFeat(FEAT_FAVORED_ENEMY_ANIMAL, oCreature)) + { + nRace = 2; + nRacialType = RACIAL_TYPE_ANIMAL; + } + else if(nRace < 3 && GetHasFeat(FEAT_FAVORED_ENEMY_BEAST, oCreature)) + { + nRace = 3; + nRacialType = RACIAL_TYPE_BEAST; + } + else if(nRace < 4 && GetHasFeat(FEAT_FAVORED_ENEMY_CONSTRUCT, oCreature)) + { + nRace = 4; + nRacialType = RACIAL_TYPE_CONSTRUCT; + } + else if(nRace < 5 && GetHasFeat(FEAT_FAVORED_ENEMY_DRAGON, oCreature)) + { + nRace = 5; + nRacialType = RACIAL_TYPE_DRAGON; + } + else if(nRace < 6 && GetHasFeat(FEAT_FAVORED_ENEMY_DWARF, oCreature)) + { + nRace = 6; + nRacialType = RACIAL_TYPE_DWARF; + } + else if(nRace < 7 && GetHasFeat(FEAT_FAVORED_ENEMY_ELEMENTAL, oCreature)) + { + nRace = 7; + nRacialType = RACIAL_TYPE_ELEMENTAL; + } + else if(nRace < 8 && GetHasFeat(FEAT_FAVORED_ENEMY_ELF, oCreature)) + { + nRace = 8; + nRacialType = RACIAL_TYPE_ELF; + } + else if(nRace < 9 && GetHasFeat(FEAT_FAVORED_ENEMY_FEY, oCreature)) + { + nRace = 9; + nRacialType = RACIAL_TYPE_FEY; + } + else if(nRace < 10 && GetHasFeat(FEAT_FAVORED_ENEMY_GIANT, oCreature)) + { + nRace = 10; + nRacialType = RACIAL_TYPE_GIANT; + } + else if(nRace < 11 && GetHasFeat(FEAT_FAVORED_ENEMY_GNOME, oCreature)) + { + nRace = 11; + nRacialType = RACIAL_TYPE_GNOME; + } + else if(nRace < 12 && GetHasFeat(FEAT_FAVORED_ENEMY_GOBLINOID, oCreature)) + { + nRace = 12; + nRacialType = RACIAL_TYPE_HUMANOID_GOBLINOID; + } + else if(nRace < 13 && GetHasFeat(FEAT_FAVORED_ENEMY_HALFELF, oCreature)) + { + nRace = 13; + nRacialType = RACIAL_TYPE_HALFELF; + } + else if(nRace < 14 && GetHasFeat(FEAT_FAVORED_ENEMY_HALFLING, oCreature)) + { + nRace = 14; + nRacialType = RACIAL_TYPE_HALFLING; + } + else if(nRace < 15 && GetHasFeat(FEAT_FAVORED_ENEMY_HALFORC, oCreature)) + { + nRace = 15; + nRacialType = RACIAL_TYPE_HALFORC; + } + else if(nRace < 16 && GetHasFeat(FEAT_FAVORED_ENEMY_HUMAN, oCreature)) + { + nRace = 16; + nRacialType = RACIAL_TYPE_HUMAN; + } + else if(nRace < 17 && GetHasFeat(FEAT_FAVORED_ENEMY_MAGICAL_BEAST, oCreature)) + { + nRace = 17; + nRacialType = RACIAL_TYPE_MAGICAL_BEAST; + } + else if(nRace < 18 && GetHasFeat(FEAT_FAVORED_ENEMY_MONSTROUS, oCreature)) + { + nRace = 18; + nRacialType = RACIAL_TYPE_HUMANOID_MONSTROUS; + } + else if(nRace < 19 && GetHasFeat(FEAT_FAVORED_ENEMY_ORC, oCreature)) + { + nRace = 19; + nRacialType = RACIAL_TYPE_HUMANOID_ORC; + } + else if(nRace < 20 && GetHasFeat(FEAT_FAVORED_ENEMY_OUTSIDER, oCreature)) + { + nRace = 20; + nRacialType = RACIAL_TYPE_OUTSIDER; + } + else if(nRace < 21 && GetHasFeat(FEAT_FAVORED_ENEMY_REPTILIAN, oCreature)) + { + nRace = 21; + nRacialType = RACIAL_TYPE_HUMANOID_REPTILIAN; + } + else if(nRace < 22 && GetHasFeat(FEAT_FAVORED_ENEMY_SHAPECHANGER, oCreature)) + { + nRace = 22; + nRacialType = RACIAL_TYPE_SHAPECHANGER; + } + else if(nRace < 23 && GetHasFeat(FEAT_FAVORED_ENEMY_UNDEAD, oCreature)) + { + nRace = 23; + nRacialType = RACIAL_TYPE_UNDEAD; + } + else if(nRace < 24 && GetHasFeat(FEAT_FAVORED_ENEMY_VERMIN, oCreature)) + { + nRace = 24; + nRacialType = RACIAL_TYPE_VERMIN; + } + else nRace = 25; + if(nRace < 25) + { + 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: " + + 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((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + 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", "1962", "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) +{ + int nCnt = 1, nInMelee, nHighestMelee; + string sCnt = "1"; + float fAllyRange; + object oTarget, oAlly = GetLocalObject(oCreature, AI_ALLY + sCnt); + 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 + " " + + 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(!GetIsDead(oAlly) && nInMelee > nHighestMelee) + { + oTarget = ai_GetEnemyAttackingMyAlly(oCreature, oAlly, fMaxRange); + if(oTarget != OBJECT_INVALID) nHighestMelee = nInMelee; + } + } + sCnt = IntToString(++nCnt); + oAlly = GetLocalObject(oCreature, AI_ALLY + sCnt); + } + // If we do not have a good target then lets see if there are more targets. + if(oTarget == OBJECT_INVALID) + { + // 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)) + { + oTarget = ai_GetFlankTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk); + } + } + if(AI_DEBUG) ai_Debug("0i_combat", "2000", "oTarget " + GetName(oTarget) + + " is attacking " + GetName(oAlly)); + return oTarget; +} +object ai_GetRangedTarget(object oCreature, float fMaxRange = AI_RANGE_PERCEPTION, int bAlwaysAtk = TRUE) +{ + 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", "2037", "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((bAlwaysAtk || !ai_IsStrongerThanMe(oCreature, nCounter)) && + ai_TargetIsInRangeofCreature(oCreature, AI_ENEMY, sCounter, fMaxRange) && + ai_TargetIsInRangeofMaster(oCreature, sTarget.oTarget)) + { + if(ai_GetIsRangeWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, 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", "2060", "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) +{ + object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + 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) + { + if(ai_CanIMoveInCombat(oCreature)) + { + sIndex = IntToString(ai_GetLowestCRIndex(oCreature, AI_RANGE_MELEE)); + } + else sIndex = IntToString(ai_GetNearestIndex(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. + // 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)); + } */ + } + 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!"); + return oTarget; +} +object ai_GetNearestTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) +{ + object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + 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 not then lets go find someone to attack! + else + { + // Get the nearest enemy. + sIndex = IntToString(ai_GetNearestIndexNotInAOE(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)); + } + } + 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", "2024", GetName(oTarget) + " is the nearest target for melee combat!"); + return oTarget; +} +object ai_GetLowestCRTargetForMeleeCombat(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) +{ + object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + 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 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. + // 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)); + } */ + } + 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 weakest target for melee combat!"); + return oTarget; +} +object ai_GetHighestCRTargetForMeleeCombat(object oCreature, int nInMelee) +{ + object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + 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 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. + // If we didn't get a target then get any target within range. + if(sIndex == "0") sIndex = IntToString(ai_GetHighestCRIndex(oCreature)); + */ + } + 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", "2070", GetName(oTarget) + " is the strongest target for melee combat!"); + return oTarget; +} +object ai_GetEnemyAttackingMe(object oCreature, float fMaxRange = AI_RANGE_MELEE) +{ + int nCtr = 1; + float fDistance; + string sCtr = "1"; + object oAttacked; + object oEnemy = GetLocalObject(oCreature, AI_ENEMY + "1"); + while(oEnemy != OBJECT_INVALID) + { + if(!ai_Disabled(oEnemy)) + { + fDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sCtr); + if(AI_DEBUG) ai_Debug("0i_combat", "2084", "Getting Enemy Attacking Me: " + sCtr + " " + + GetName(oEnemy) + " fTargetRange: " + FloatToString(fDistance, 0, 2) + + " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " Attacking: " + + GetName(ai_GetAttackedTarget(oEnemy))); + if(fDistance <= fMaxRange) + { + oAttacked = ai_GetAttackedTarget(oEnemy); + // 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))); + return oEnemy; + } + } + } + sCtr = IntToString(++nCtr); + oEnemy = GetLocalObject(oCreature, AI_ENEMY + sCtr); + } + return OBJECT_INVALID; +} +object ai_GetEnemyAttackingMyAlly(object oCreature, object oAlly, float fMaxRange = AI_RANGE_MELEE) +{ + int nCtr = 1, nIndex, nDIndex; + int bIngnoreAssociates = ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES); + float fEnemyRange, fNearestEnemyRange = fMaxRange + 1.0; + float fNearestDEnemyRange = fMaxRange + 1.0; + string sCtr = "1"; + object oAttacked; + object oEnemy = GetLocalObject(oCreature, AI_ENEMY + "1"); + while(oEnemy != OBJECT_INVALID) + { + fEnemyRange = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sCtr); + if(AI_DEBUG) ai_Debug("0i_combat", "2117", "Getting Enemy Attacking Ally:" + + GetName(oAlly) + ": " + sCtr + " InMelee:" + + GetName(oEnemy) + " fEnemyRange: " + FloatToString(fEnemyRange, 0, 2) + + " fMaxRange: " + FloatToString(fMaxRange, 0, 2) + " Attacking: " + + GetName(ai_GetAttackedTarget(oEnemy))); + if(fEnemyRange <= fMaxRange) + { + oAttacked = ai_GetAttackedTarget(oEnemy); + if(AI_DEBUG) ai_Debug("0i_combat", "2125", "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! + if(oAttacked == oAlly) + { + // Lets put any disabled targets in its own group, if we + // ignore associates lets put them here as well. + if(GetLocalInt(oCreature, AI_ENEMY_DISABLED + sCtr) || + (bIngnoreAssociates && GetAssociateType(oEnemy))) + { + if(fEnemyRange < fNearestDEnemyRange) + { + fNearestDEnemyRange = fEnemyRange; + nDIndex = nCtr; + } + } + else if(fEnemyRange < fNearestEnemyRange) + { + fNearestEnemyRange = fEnemyRange; + nIndex = nCtr; + } + } + } + sCtr = IntToString(++nCtr); + oEnemy = GetLocalObject(oCreature, AI_ENEMY + sCtr); + } + // If we do not have a good target then lets see if there are more targets. + if(nIndex == 0 && nDIndex != 0) + { + // If we just checked within melee then lets check what we can see. + if (fMaxRange == AI_RANGE_MELEE) return ai_GetEnemyAttackingMyAlly(oCreature, oAlly, AI_RANGE_PERCEPTION); + else nIndex = nDIndex; + } + return GetLocalObject(oCreature, AI_ENEMY + IntToString(nIndex)); +} +int ai_GetNumOfEnemiesInRange(object oCreature, float fMaxRange = AI_RANGE_MELEE) +{ + int nNumOfEnemies, nCnt = 1; + float fDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + "1"); + while(fDistance != 0.0) + { + 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."); + return nNumOfEnemies; +} +object ai_GetAllyBuffTarget(object oCreature, int nSpell, float fMaxRange = AI_RANGE_BATTLEFIELD) +{ + // Make sure we don't over extend our movement running across the + // battlefield to cast a spell on someone does not look good. + float fNearestEnemy = GetDistanceBetween(oCreature, GetLocalObject(oCreature, AI_ENEMY_NEAREST)) - 3.0f; + // If we are in melee then extend to melee incase an ally is just past the enemy. + if(fNearestEnemy <= AI_RANGE_MELEE) fNearestEnemy = AI_RANGE_MELEE; + 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(sBuffTarget == "0") return oCreature; + if(sBuffTarget == "1") + return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_STRENGTH, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "2") + return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_DEXTERITY, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "3") + return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_CONSTITUTION, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "4") + return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_INTELLIGENCE, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "5") + return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_WISDOM, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "6") + return ai_BuffHighestAbilityScoreTarget(oCreature, nSpell, ABILITY_CHARISMA, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "7") + return ai_BuffLowestACTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "8") + return ai_BuffLowestACWithOutACBonus(oCreature, nSpell, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "9") + return ai_BuffHighestAttackTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "10") + return ai_BuffMostWoundedTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "11") + return ai_BuffLowestFortitudeSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "12") + return ai_BuffLowestReflexSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "13") + return ai_BuffLowestWillSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY); + else if(sBuffTarget == "14") + return ai_BuffLowestSaveTarget(oCreature, nSpell, "", fMaxRange, AI_ALLY); + return OBJECT_INVALID; +} + +//****************************************************************************** +//******************** OTHER COMBAT FUNCTIONS ******************************** +//****************************************************************************** + +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)); + return nRound; +} +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)) + + " - Ally Power: " + IntToString(GetLocalInt(oCreature, AI_ALLY_POWER)) + + ") + 13 + nAdj: " + IntToString(nAdjustment) + + " = " + IntToString(nDifficulty) + "(Min of 1)"); + return nDifficulty; +} +int ai_GetMyCombatRating(object oCreature) +{ + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + int nAtkBonus = GetBaseAttackBonus(oCreature); + if(GetHasFeat(FEAT_WEAPON_FINESSE, oCreature) && ai_GetIsFinesseWeapon(oCreature, oWeapon)) + { + nAtkBonus += GetAbilityModifier(ABILITY_DEXTERITY, 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) + + " nAC: " + IntToString(GetAC(oCreature)) + " - 10) / 2 = " + + IntToString((nAtkBonus + GetAC(oCreature) - 10) / 2)); + return(nAtkBonus + GetAC(oCreature) - 10) / 2; +} +object ai_GetAttackedTarget(object oCreature, int bPhysical = TRUE, int bSpell = FALSE) +{ + object oTarget = GetAttackTarget(oCreature); + if(!GetIsObjectValid(oTarget) && bPhysical) oTarget = GetLocalObject(oCreature, AI_ATTACKED_PHYSICAL); + if(!GetIsObjectValid(oTarget) && bSpell) oTarget = GetLocalObject(oCreature, AI_ATTACKED_SPELL); + if(!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) return OBJECT_INVALID; + return oTarget; +} +int ai_CheckClassType(object oTarget, int nClassType) +{ + int nCnt = 1, nClass = GetClassByPosition(1, oTarget); + // We check for the group class types. + if(nClassType < 0) + { + while(nCnt <= AI_MAX_CLASSES_PER_CHARACTER) + { + int nCaster = StringToInt(Get2DAString("classes", "SpellCaster", nClass)); + if(nClassType == AI_CLASS_TYPE_WARRIOR && !nCaster) return TRUE; + else if(nClassType == AI_CLASS_TYPE_CASTER && nCaster) return TRUE; + int nSpellType = StringToInt(Get2DAString("classes", "Arcane", nClass)); + if(nClassType == AI_CLASS_TYPE_ARCANE && nSpellType) return TRUE; + else if(nClassType == AI_CLASS_TYPE_DIVINE && !nSpellType) return TRUE; + nClass = GetClassByPosition(++nCnt, oTarget); + } + } + // Checks for normal classes. + else + { + while(nCnt <= AI_MAX_CLASSES_PER_CHARACTER) + { + if(nClass == nClassType) return TRUE; + nClass = GetClassByPosition(++nCnt, oTarget); + } + } + return FALSE; +} +int ai_CheckRacialType(object oTarget, int nRacialType) +{ + int nRace = GetRacialType(oTarget); + if(nRacialType == nRace) return TRUE; + else if(nRacialType == AI_RACIAL_TYPE_ANIMAL_BEAST) + { + if(nRace == RACIAL_TYPE_ANIMAL || + nRace == RACIAL_TYPE_BEAST || + nRace == RACIAL_TYPE_MAGICAL_BEAST) return TRUE; + } + else if(nRacialType == AI_RACIAL_TYPE_HUMANOID) + { + switch (nRace) + { + case RACIAL_TYPE_DWARF : + case RACIAL_TYPE_ELF : + case RACIAL_TYPE_GNOME : + case RACIAL_TYPE_HALFELF : + case RACIAL_TYPE_HALFLING : + case RACIAL_TYPE_HALFORC : + case RACIAL_TYPE_HUMAN : + case RACIAL_TYPE_HUMANOID_GOBLINOID : + case RACIAL_TYPE_HUMANOID_MONSTROUS : + case RACIAL_TYPE_HUMANOID_ORC : + case RACIAL_TYPE_HUMANOID_REPTILIAN : + return TRUE; + } + } + return FALSE; +} +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)); + SetLocalInt(oCreature, AI_NORMAL_FORM, nForm + 1); + } +} +int ai_GetNormalAppearance(object oCreature) +{ + int nForm = GetLocalInt(oCreature, AI_NORMAL_FORM) - 1; + if(nForm == -1) + { + ai_SetNormalAppearance(oCreature); + nForm = GetLocalInt(oCreature, AI_NORMAL_FORM) - 1; + } + return nForm; +} +struct stClasses ai_GetFactionsClasses(object oCreature, int bEnemy = TRUE, float fMaxRange = AI_RANGE_BATTLEFIELD) +{ + struct stClasses sCount; + int nCnt = 1, nPosition, nClass, nLevels; + object oTarget; + if(bEnemy) oTarget = ai_GetNearestEnemy(oCreature, 1, 7, 7); + else oTarget = ai_GetNearestAlly(oCreature, 1, 7, 7); + while(oTarget != OBJECT_INVALID && GetDistanceBetween(oTarget, oCreature) <= fMaxRange) + { + for(nPosition = 1; nPosition <= AI_MAX_CLASSES_PER_CHARACTER; nPosition++) + { + nClass = GetClassByPosition(nPosition, oTarget); + nLevels = GetLevelByPosition(nPosition, oTarget); + if(nClass == CLASS_TYPE_ANIMAL || + nClass == CLASS_TYPE_BARBARIAN || + nClass == CLASS_TYPE_COMMONER || + nClass == CLASS_TYPE_CONSTRUCT || + nClass == CLASS_TYPE_ELEMENTAL || + nClass == CLASS_TYPE_FIGHTER || + nClass == CLASS_TYPE_GIANT || + nClass == CLASS_TYPE_HUMANOID || + nClass == CLASS_TYPE_MONSTROUS || + nClass == CLASS_TYPE_PALADIN || + nClass == CLASS_TYPE_RANGER || + nClass == CLASS_TYPE_ROGUE || + nClass == CLASS_TYPE_VERMIN || + nClass == CLASS_TYPE_MONK || + nClass == CLASS_TYPE_SHAPECHANGER) + { + sCount.FIGHTERS += 1; + sCount.FIGHTER_LEVELS += nLevels; + } + else if(nClass == CLASS_TYPE_CLERIC || + nClass == CLASS_TYPE_DRUID) + { + sCount.CLERICS += 1; + sCount.CLERIC_LEVELS += nLevels; + } + else if(nClass == CLASS_TYPE_BARD || + nClass == CLASS_TYPE_FEY || + nClass == CLASS_TYPE_SORCERER || + nClass == CLASS_TYPE_WIZARD) + { + sCount.MAGES += 1; + sCount.MAGE_LEVELS += nLevels; + } + else if(nClass == CLASS_TYPE_ABERRATION || + nClass == CLASS_TYPE_DRAGON || + nClass == 29 || //oozes + nClass == CLASS_TYPE_MAGICAL_BEAST || + nClass == CLASS_TYPE_OUTSIDER) + { + sCount.MONSTERS += 1; + sCount.MONSTER_LEVELS += nLevels; + } + sCount.TOTAL_LEVELS += nLevels; + } + sCount.TOTAL += 1; + 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) + + " CLERICS: " + IntToString(sCount.CLERICS) + "(" + IntToString(sCount.CLERIC_LEVELS) + + ") FIGHTERS: " +IntToString(sCount.FIGHTERS) + "(" + IntToString(sCount.FIGHTER_LEVELS) + + ") MAGES: " +IntToString(sCount.MAGES) + "(" + IntToString(sCount.MAGE_LEVELS) + + ") MONSTERS: " +IntToString(sCount.MONSTERS) + "(" + IntToString(sCount.MONSTER_LEVELS) + + ") TOTALS: " +IntToString(sCount.TOTAL) + "(" + IntToString(sCount.TOTAL_LEVELS)); + return sCount; +} +string ai_GetMostDangerousClass(struct stClasses stCount) +{ + string sClass; + // Lets weight the fighter levels 30% higher. + int nFighter =((stCount.FIGHTER_LEVELS) * 13)/10; + if(nFighter >= stCount.CLERIC_LEVELS) + { + if(nFighter >= stCount.MAGE_LEVELS) + { + if(nFighter >= stCount.MONSTER_LEVELS) return "FIGHTER"; + else return "MONSTER"; + } + else if(stCount.MAGE_LEVELS >= stCount.MONSTER_LEVELS) return "MAGE"; + else return "MONSTER"; + } + else if(stCount.CLERIC_LEVELS >= stCount.MAGE_LEVELS) + { + if(stCount.CLERIC_LEVELS >= stCount.MONSTER_LEVELS) return "CLERIC"; + else return "MONSTER"; + } + else if(stCount.MAGE_LEVELS >= stCount.MONSTER_LEVELS) return "MAGE"; + else return "MONSTER"; + return ""; +} +void ai_EquipBestWeapons(object oCreature, object oTarget = OBJECT_INVALID) +{ + // Lets not check for weapons on creatures that can't use them! + int nRacialType = GetRacialType(oCreature); + if(nRacialType == RACIAL_TYPE_ANIMAL || + nRacialType == RACIAL_TYPE_DRAGON || + nRacialType == RACIAL_TYPE_MAGICAL_BEAST || + 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!"); + // 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) + + " bIsWieldingMelee: " + IntToString(bIsWieldingMelee)); + // If we are hidden then change to a melee weapon so we can move in to attack. + if(ai_GetIsHidden(oCreature)) + { + // Equip a melee weapon unless we already have one. + if(!bIsWieldingMelee) ai_EquipBestMeleeWeapon(oCreature, oTarget); + return; + } + // 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: " + + IntToString(GetHasFeat(FEAT_POINT_BLANK_SHOT, oCreature))); + // We are in melee combat. + if(nEnemyGroup > 0) + { + if(bIsWieldingRanged) + { + // We have the point blank shot feat or there are more than one enemy on us. + // Note: Point Blank shot feat is bad once we have more than one enemy on us. + if(!GetHasFeat(FEAT_POINT_BLANK_SHOT, oCreature) || nEnemyGroup > 1) + { + // If I'm not using a melee weapon. + if(!bIsWieldingMelee) + { + ai_EquipBestMeleeWeapon(oCreature); + if(AI_DEBUG) ai_Debug("0i_combat", "2699", GetName(oCreature) + " is equiping melee weapon due to close enemies!"); + } + } + } + } + // 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 are at range with the enemy then equip a ranged weapon. + if(!bIsWieldingRanged) + { + ai_EquipBestRangedWeapon(oTarget); + // 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 we equiped a ranged weapon then drop out. + } + } + // We don't have a weapon out so equip one! We are in combat! + if(!bIsWieldingRanged && !bIsWieldingMelee) ai_EquipBestMeleeWeapon(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!"); + 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)); + bTwoWeaponUser = GetHasFeat(374/*FEAT_DUAL_WIELD*/, oCreature) || GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oCreature); + object oShield = OBJECT_INVALID; + object oRight = OBJECT_INVALID; + object oLeft = OBJECT_INVALID; + object o2Handed = OBJECT_INVALID; + object o2HandedHand = OBJECT_INVALID; + object oRightHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND); + if(oRightHand != OBJECT_INVALID) + { + // Setup the item in our right hand's avg dmg and gold value as our base. + if(ai_GetIsTwoHandedWeapon(oRightHand, oCreature)) + { + if(ai_GetIsDoubleWeapon(oRightHand)) + { + f2HandedPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand, TRUE, FALSE, oRightHand); + } + else f2HandedPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand, TRUE); + n2HandedValue = GetGoldPieceValue(oRightHand); + if(AI_DEBUG) ai_Debug("0i_combat", "3073", " 2Handed oRightHand: " + GetName(oRightHand) + + " f2HandPower: " + FloatToString(f2HandedPower, 0, 2) + + " n2HandedValue: " + IntToString(n2HandedValue)); + } + else if(ai_GetIsSingleHandedWeapon(oRightHand, oCreature)) + { + fRightPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand); + nRightValue = GetGoldPieceValue(oRightHand); + if(AI_DEBUG) ai_Debug("0i_combat", "3081", " 1Handed oRightHand: " + GetName(oRightHand) + + " fRightPower: " + FloatToString(fRightPower, 0, 2) + + " nRightValue: " + IntToString(nRightValue)); + } + } + object oLeftHand = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature); + if(oLeftHand != OBJECT_INVALID) + { + // Setup the item in our left hand's Shield AC and gold value as our base. + if(ai_GetIsShield(oLeftHand)) + { + nShieldPower = ai_SetShieldAC(oCreature, oLeftHand); + nShieldValue = GetGoldPieceValue(oLeftHand); + if(AI_DEBUG) ai_Debug("0i_combat", "3098", " Shield oLeftHand: " + GetName(oLeftHand) + + " fShieldPower: " + IntToString(nShieldPower) + + " nShieldValue: " + IntToString(nShieldValue)); + } + // Setup the item in our left hand's avg dmg and gold value as our base. + else + { + fLeftPower = ai_GetMeleeWeaponAvgDmg(oCreature, oLeftHand, FALSE, TRUE); + nLeftValue = GetGoldPieceValue(oLeftHand); + if(AI_DEBUG) ai_Debug("0i_combat", "3103", " 1Handed oLeftHand: " + GetName(oLeftHand) + + " fLeftPower: " + FloatToString(fLeftPower, 0, 2) + + " nLeftValue: " + IntToString(nLeftValue)); + } + } + int nWeaponSize, nType, nCreatureSize = GetCreatureSize(oCreature); + // Get the best weapons they have in their inventory. + object oItem = GetFirstItemInInventory(oCreature); + // If they don't have any items then lets stop, we can't equip a weapon/shield. + if(oItem == OBJECT_INVALID) return FALSE; + while(oItem != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3114", GetName(oItem) + " MeleeWeapon: " + + IntToString(ai_GetIsMeleeWeapon(oItem)) + " Proficient: " + + IntToString(ai_GetIsProficientWith(oCreature, oItem)) + + " Identified: " + IntToString(GetIdentified(oItem))); + if(ai_GetIsProficientWith(oCreature, oItem) && + GetIdentified(oItem) && ai_CheckIfCanUseItem(oCreature, oItem)) + { + nItemValue = GetGoldPieceValue(oItem); + if(AI_DEBUG) ai_Debug("0i_combat", "3122", " nItemValue: " + IntToString(nItemValue)); + if(!GetLocalInt(GetModule(), AI_RULE_ILR) || nMaxItemValue >= nItemValue) + { + if(ai_GetIsShield(oItem)) + { + nItemPower = ai_SetShieldAC(oCreature, oItem); + if(nItemPower > nShieldPower || + (nItemPower == nShieldPower && nItemValue > nShieldValue)) + { oShield = oItem; nShieldPower = nItemPower; nShieldValue = nItemValue; } + } + else if(ai_GetIsMeleeWeapon(oItem)) + { + // Make sure the creature and weapon are close enough in size. + // Can wield a weapon up to one size larger than their size. + // Can wield a weapon down to two sizes smaller than their size. + nType = GetBaseItemType(oItem); + nWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nType)); + if(nWeaponSize >= nCreatureSize - 2 && nWeaponSize <= nCreatureSize + 1) + { + // Get item avg damage based on if it is 2handed or 1handed. + if(ai_GetIsSingleHandedWeapon(oItem, oCreature)) + { + fItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem); + fOffItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem, FALSE, TRUE); + // If the new weapon is better than the weapon in our right hand. + if(fItemPower > fRightPower || + (fItemPower == fRightPower && nItemValue > nRightValue)) + { + // We need to check if the weapon in the right hand is + // better than the weapon in the left hand since we are + // replacing our right hand weapon. + // Note: we must find out if we have selected a weapon for the + // right hand i.e. oRight or the best weapon is in our + // right hand i.e. oRightHand! + fOffItemPower = 0.0; + if(oRight != OBJECT_INVALID && ai_GetIsSingleHandedWeapon(oRight, oCreature)) + { + fOffItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRight, FALSE, TRUE); + } + else if(oRightHand != OBJECT_INVALID && ai_GetIsSingleHandedWeapon(oRightHand, oCreature)) + { + fOffItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oRightHand, FALSE, TRUE); + } + // If the right hand weapon is better than the weapon in our left hand. + if(fOffItemPower > fLeftPower || (fOffItemPower > 0.0 && + fOffItemPower == fLeftPower && nRightValue > nLeftValue)) + { + if(oRight != OBJECT_INVALID) oLeft = oRight; + else oLeft = oRightHand; + fLeftPower = fOffItemPower; + nLeftValue = nRightValue; + } + oRight = oItem; + fRightPower = fItemPower; + nRightValue = nItemValue; + } + // If the new weapon is better than the weapon in our left hand. + else if(fOffItemPower > fLeftPower || + (fOffItemPower == fLeftPower && nItemValue > nLeftValue)) + { oLeft = oItem; fLeftPower = fOffItemPower; nLeftValue = nItemValue; } + } + else if(ai_GetIsTwoHandedWeapon(oItem, oCreature)) + { + if(ai_GetIsDoubleWeapon(oItem)) + { + fItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem, TRUE, FALSE, oItem); + } + else fItemPower = ai_GetMeleeWeaponAvgDmg(oCreature, oItem, TRUE); + // If the new weapon is better than the selected weapon. + if(fItemPower > f2HandedPower || + (fItemPower == f2HandedPower && nItemValue > n2HandedValue)) + { + o2Handed = oItem; + f2HandedPower = fItemPower; + n2HandedValue = nItemValue; + } + } + } + } + } + } + oItem = GetNextItemInInventory(); + } + if(AI_DEBUG) ai_Debug("0i_combat", "3197", "oRight: " + GetName(oRight) + " oLeft:" + + GetName(oLeft) + " oShield: " + GetName(oShield) + + "o2Handed: " + GetName(o2Handed)); + // First check for two weapons first. + if(bTwoWeaponUser && oRight != OBJECT_INVALID && oLeft != 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: " + + FloatToString(fRightPower, 0, 2) + " 2HandedPower: " + + FloatToString(f2HandedPower, 0, 2)); + if(fRightPower > f2HandedPower) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3210", GetName(oCreature) + " is equiping " + + GetName(oRight) + " in the right hand and " + GetName(oLeft) + + " in the left hand."); + ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND); + ActionEquipItem(oLeft, INVENTORY_SLOT_LEFTHAND); + return TRUE; + } + } + if(f2HandedPower > fRightPower && o2Handed != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3220", GetName(oCreature) + " is equiping " + + GetName(o2Handed) + " in both hands."); + ActionEquipItem(o2Handed, INVENTORY_SLOT_RIGHTHAND); + return TRUE; + } + // 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 " + + GetName(oRight) + " in the right hand. "); + ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND); + } + // Make sure we are not equiping a 2handed weapon and + // If not can we equip a shield? + if((oRight == OBJECT_INVALID || ai_GetIsSingleHandedWeapon(oRight, oCreature) || + !ai_GetIsTwoHandedWeapon(oRightHand, oCreature)) && + oShield != OBJECT_INVALID && GetHasFeat(FEAT_SHIELD_PROFICIENCY, oCreature)) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3238", GetName(oCreature) + " is equiping " + + GetName(oShield) + " in the left hand."); + ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); + return TRUE; + } + // Finally if we don't have a weapon to equip so check to see if we are + // holding a bow. + else if(oRight == OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3247", 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)); + ActionUnequipItem(oRightHand); + return TRUE; + } + } + if(AI_DEBUG) ai_Debug("0i_combat", "3257", 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!"); + int nAmmo, nAmmoSlot, nBestType1, nBestType2, nType, nFeat, nItemValue, nRangedValue; + int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature)); + string sAmmo; + object oRightHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + if(oRightHand != OBJECT_INVALID && ai_GetIsRangeWeapon(oRightHand)) + { + // Setup the item in our right hand as our base gold value to check against. + if(ai_GetIsRangeWeapon(oRightHand)) nRangedValue = GetGoldPieceValue(oRightHand); + } + object oRanged = OBJECT_INVALID, oAmmo = OBJECT_INVALID; + // Find the best type of ranged weapon for this player. + if(GetHasFeat(FEAT_WEAPON_FOCUS_LONGBOW, oCreature)) + { nBestType1 = BASE_ITEM_LONGBOW; nAmmo = BASE_ITEM_ARROW; nAmmoSlot = INVENTORY_SLOT_ARROWS; sAmmo = "arrow";} + else if(GetHasFeat(FEAT_WEAPON_FOCUS_SHORTBOW, oCreature)) + { nBestType1 = BASE_ITEM_SHORTBOW; nAmmo = BASE_ITEM_ARROW; nAmmoSlot = INVENTORY_SLOT_ARROWS; sAmmo = "arrow";} + else if(GetHasFeat(FEAT_WEAPON_FOCUS_HEAVY_CROSSBOW, oCreature)) + { nBestType1 = BASE_ITEM_HEAVYCROSSBOW; nAmmo = BASE_ITEM_BOLT; nAmmoSlot = INVENTORY_SLOT_BOLTS; sAmmo = "bolt";} + else if(GetHasFeat(FEAT_WEAPON_FOCUS_LIGHT_CROSSBOW, oCreature)) + { nBestType1 = BASE_ITEM_LIGHTCROSSBOW; nAmmo = BASE_ITEM_BOLT; nAmmoSlot = INVENTORY_SLOT_BOLTS; sAmmo = "bolt";} + else if(GetHasFeat(FEAT_WEAPON_FOCUS_SLING, oCreature)) + { nBestType1 = BASE_ITEM_SLING; nAmmo = BASE_ITEM_BULLET; nAmmoSlot = INVENTORY_SLOT_BULLETS; sAmmo = "bullet";} + else if(GetHasFeat(FEAT_WEAPON_FOCUS_DART, oCreature)) + { nBestType1 = BASE_ITEM_DART; } + else if(GetHasFeat(FEAT_WEAPON_FOCUS_SHURIKEN, oCreature)) + { nBestType1 = BASE_ITEM_SHURIKEN; } + else if(GetHasFeat(FEAT_WEAPON_FOCUS_THROWING_AXE, oCreature)) + { nBestType1 = BASE_ITEM_THROWINGAXE; } + // These feats require a bow. + else if(GetHasFeat(FEAT_RAPID_SHOT, oCreature)) + { nBestType1 = BASE_ITEM_LONGBOW; nBestType2 = BASE_ITEM_SHORTBOW; + nAmmo = BASE_ITEM_ARROW; nAmmoSlot = INVENTORY_SLOT_ARROWS; sAmmo = "arrow"; } + // This feat requires a xbow. + 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) + + " nAmmo: " + IntToString(nAmmo)); + int nCreatureSize = GetCreatureSize(oCreature) + 1; + // Cycle through the inventory looking for a ranged weapon. + object oItem = GetFirstItemInInventory(oCreature); + while(oItem != OBJECT_INVALID) + { + nType = GetBaseItemType(oItem); + if(AI_DEBUG) ai_Debug("0i_combat", "3269", "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: " + + IntToString(ai_GetIsProficientWith(oCreature, oItem)) + + " nMaxItemValue: " + IntToString(nMaxItemValue)); + if(ai_GetIsProficientWith(oCreature, oItem)) + { + if(ai_CheckIfCanUseItem(oCreature, oItem)) + { + nItemValue = GetGoldPieceValue(oItem); + if(AI_DEBUG) ai_Debug("0i_combat", "3284", "nItemValue: " + IntToString(nItemValue)); + if(!GetLocalInt(GetModule(), AI_RULE_ILR) || nMaxItemValue >= nItemValue) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3287", " 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) + + " 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. + if(nType == nBestType1 || nType == nBestType2 || + nBestType1 == 0 || oRanged == OBJECT_INVALID) + { + if(nItemValue > nRangedValue) + { + if(ai_GetHasItemProperty(oItem, ITEM_PROPERTY_UNLIMITED_AMMUNITION)) + { + oRanged = oItem; nRangedValue = nItemValue; + if(AI_DEBUG) ai_Debug("0i_combat", "3304", "Selecting oRanged: " + GetName(oRanged) + + " nRangedValue: " + IntToString(nRangedValue) + " and doesn't need ammo!"); + } + else + { + if(nBestType1 == 0) + { + if(nType == BASE_ITEM_LONGBOW || nType == BASE_ITEM_SHORTBOW) + { nAmmo = BASE_ITEM_ARROW; sAmmo = "arrow"; nAmmoSlot = INVENTORY_SLOT_ARROWS; } + else if(nType == BASE_ITEM_HEAVYCROSSBOW || nType == BASE_ITEM_LIGHTCROSSBOW) + { nAmmo = BASE_ITEM_BOLT; sAmmo = "bolt"; nAmmoSlot = INVENTORY_SLOT_BOLTS; } + else if(nType == BASE_ITEM_SLING) + { nAmmo = BASE_ITEM_BULLET; sAmmo = "bullet"; nAmmoSlot = INVENTORY_SLOT_BULLETS; } + else nAmmo = 0; + } + // Now do we have ammo for it? + if(AI_DEBUG) ai_Debug("0i_combat", "3320", "nAmmo: " + IntToString(nAmmo)); + if(nAmmo > 0) + { + if(nAmmo == BASE_ITEM_ARROW || + nAmmo == BASE_ITEM_BOLT || + nAmmo == BASE_ITEM_BULLET) oAmmo = GetItemInSlot(nAmmoSlot); + if(oAmmo == OBJECT_INVALID) + { + // We don't have ammo equiped so lets see if we have any in our inventory. + oAmmo = GetFirstItemInInventory(); + while(oAmmo != OBJECT_INVALID) + { + if(GetBaseItemType(oAmmo) == nAmmo) break; + oAmmo = GetNextItemInInventory(); + } + if(oAmmo != OBJECT_INVALID) ActionEquipItem(oAmmo, nAmmoSlot); + } + } + if(oAmmo != OBJECT_INVALID) + { + oRanged = oItem; nRangedValue = nItemValue; + if(AI_DEBUG) ai_Debug("0i_combat", "3307", "Selecting oRanged: " + GetName(oRanged) + + " nRangedValue: " + IntToString(nRangedValue)); + } + } + } + } + } + } + } + } + } + oItem = GetNextItemInInventory(oCreature); + } + // 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!"); + return FALSE; + } + ActionEquipItem(oRanged, INVENTORY_SLOT_RIGHTHAND); + return TRUE; +} +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!"); + int nValue, nRightValue; + int nMaxItemValue = ai_GetMaxItemValueThatCanBeEquiped(GetHitDice(oCreature)); + object oRight = OBJECT_INVALID; + object oRightHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + if(oRightHand != OBJECT_INVALID) + { + nRightValue = GetGoldPieceValue(oRightHand); + } + // Get the best kama they have in their inventory. + object oItem = GetFirstItemInInventory(oCreature); + // If they don't have any kamas then lets stop, we can't equip a weapon. + if(oItem == OBJECT_INVALID) return FALSE; + while(oItem != OBJECT_INVALID) + { + nValue = GetGoldPieceValue(oItem); + // Make sure they are high enough level to equip this item. + if(nMaxItemValue >= nValue && nValue > 1) + { + // Is it a single handed weapon? + if(GetBaseItemType(oItem) == BASE_ITEM_KAMA) + { + // Replace the lowest value right weapon. + if(nValue > nRightValue) + { + oRight = oItem; nRightValue = nValue; + } + } + } + oItem = GetNextItemInInventory(oCreature); + } + // 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!"); + return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_combat", "2986", GetName(oCreature) + " is equiping " + GetName(oRight) + " in the right hand."); + ActionEquipItem(oRight, INVENTORY_SLOT_RIGHTHAND); + return TRUE; +} +int ai_IsInADangerousAOE(object oCreature, float fMaxRange = AI_RANGE_BATTLEFIELD, int bMove = FALSE) +{ + int bDangerous, nSpell, nCnt = 1; + string sAOEType; + object oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT, oCreature, nCnt); + float fRadius, fDistance = GetDistanceBetween(oCreature, oAOE); + while(oAOE != OBJECT_INVALID && fDistance <= fMaxRange) + { + // AOE's have the tag set to the "LABEL" in vfx_persistent.2da + // I check vs those labels to see if the AOE is offensive. + // Below is the list of Offensive AOE effects. + sAOEType = GetTag(oAOE); + if(sAOEType == "VFX_PER_WEB") { fRadius = 6.7; nSpell = SPELL_WEB; } + else if(sAOEType == "VFX_PER_ENTANGLE") { fRadius = 5.0; nSpell = SPELL_ENTANGLE; } + else if(sAOEType == "VFX_PER_GREASE") { fRadius = 6.0; nSpell = SPELL_GREASE; } + else if(sAOEType == "VFX_PER_EVARDS_BLACK_TENTACLES") + { fRadius = 5.0; nSpell = SPELL_EVARDS_BLACK_TENTACLES; } + //else if(sAOEType == "VFX_PER_DARKNESS") { fRadius = 6.7; nSpell = SPELL_DARKNESS; } + //else if(sAOEType == "VFX_MOB_SILENCE") { fRadius = 4.0; nSpell = SPELL_SILENCE; } + else if(sAOEType == "VFX_PER_FOGSTINK") { fRadius = 6.7; nSpell = SPELL_STINKING_CLOUD; } + else if(sAOEType == "VFX_PER_FOGFIRE") { fRadius = 5.0; nSpell = SPELL_INCENDIARY_CLOUD; } + else if(sAOEType == "VFX_PER_FOGKILL") { fRadius = 5.0; nSpell = SPELL_CLOUDKILL; } + else if(sAOEType == "VFX_PER_FOGMIND") { fRadius = 5.0; nSpell = SPELL_MIND_FOG; } + else if(sAOEType == "VFX_PER_CREEPING_DOOM") { fRadius = 6.7; nSpell = SPELL_CREEPING_DOOM; } + else if(sAOEType == "VFX_PER_FOGACID") { fRadius = 5.0; nSpell = SPELL_ACID_FOG; } + else if(sAOEType == "VFX_PER_FOGBEWILDERMENT") { fRadius = 5.0; nSpell = SPELL_CLOUD_OF_BEWILDERMENT; } + else if(sAOEType == "VFX_PER_WALLFIRE") { fRadius = 10.0; nSpell = SPELL_WALL_OF_FIRE; } + else if(sAOEType == "VFX_PER_WALLBLADE") { fRadius = 10.0; nSpell = SPELL_BLADE_BARRIER; } + 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) + + " AOE Radius: " + FloatToString(fRadius, 0, 2) + + " AOE Type: " + GetTag(oAOE)); + // fRadius > 0.0 keeps them from tiggering that they are in a dangerous + // AOE due to having an AOE on them. + if(fRadius > 0.0 && fDistance <= fRadius && + !ai_CreatureImmuneToEffect(GetAreaOfEffectCreator(oAOE), oCreature, nSpell)) + { + bDangerous = TRUE; + if(nSpell == SPELL_WEB || nSpell == SPELL_ENTANGLE) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) bDangerous = FALSE; + if(GetReflexSavingThrow(oCreature) + GetAbilityModifier(ABILITY_DEXTERITY, oCreature) >= ai_GetCharacterLevels(oCreature)) + bDangerous = FALSE; + } + break; + } + oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT, oCreature, ++nCnt); + fDistance = GetDistanceBetween(oCreature, oAOE); + } + if(bDangerous && bMove) + { + location lLocation; + object oTarget; + if(ai_GetIsInCombat(oCreature)) + { + object oMaster = GetMaster(oCreature); + // If we have a ranged weapon then backout and use that. + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + lLocation = GetRandomLocation(GetArea(oCreature), oCreature, fRadius + 1.0); + } + 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); + 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!"); + ActionMoveToLocation(lLocation, TRUE); + return TRUE; + } + else if(bDangerous) return TRUE; + return FALSE; +} +int ai_GetIsHidden(object oHidden) +{ + int nEffectType; + effect eEffect = GetFirstEffect(oHidden); + while(GetIsEffectValid(eEffect)) + { + nEffectType = GetEffectType(eEffect); + if(nEffectType == EFFECT_TYPE_INVISIBILITY) return 1; + else if(nEffectType == EFFECT_TYPE_IMPROVEDINVISIBILITY) return 1; + else if(nEffectType == EFFECT_TYPE_DARKNESS) return 2; + else if(nEffectType == EFFECT_TYPE_SANCTUARY) return 3; + else if(nEffectType == EFFECT_TYPE_ETHEREAL) return 3; + eEffect = GetNextEffect(oHidden); + } + if(GetActionMode(oHidden, ACTION_MODE_STEALTH)) return 4; + return FALSE; +} +int ai_CastOffensiveSpellVsTarget(object oCaster, object oCreature, int nSpell) +{ + // Check saves. + string sSave = Get2DAString("ai_spells", "SaveType", nSpell); + // There is no save! + if(sSave == "") return TRUE; + // Get the level of the spell. + int nSpellLvl = StringToInt(Get2DAString("spells", "Innate", nSpell)); + // Randomize our check. + nSpellLvl += Random(AI_SPELL_CHECK_DIE) + AI_SPELL_CHECK_BONUS; + // Check feats that might increase our DC. + string sSchool = Get2DAString("spells", "School", nSpell); + if(sSchool == "V") + { + if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_EVOCATION, oCaster)) nSpellLvl += 4; + else if(GetHasFeat(FEAT_SPELL_FOCUS_EVOCATION, oCaster)) nSpellLvl += 2; + } + else if(sSchool == "C") + { + if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_CONJURATION, oCaster)) nSpellLvl += 4; + else if(GetHasFeat(FEAT_SPELL_FOCUS_CONJURATION, oCaster)) nSpellLvl += 2; + } + else if(sSchool == "N") + { + if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_NECROMANCY, oCaster)) nSpellLvl += 4; + else if(GetHasFeat(FEAT_SPELL_FOCUS_NECROMANCY, oCaster)) nSpellLvl += 2; + } + else if(sSchool == "E") + { + if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ENCHANTMENT, oCaster)) nSpellLvl += 4; + else if(GetHasFeat(FEAT_SPELL_FOCUS_ENCHANTMENT, oCaster)) nSpellLvl += 2; + } + else if(sSchool == "I") + { + if(GetHasFeat(FEAT_GREATER_SPELL_FOCUS_ILLUSION, oCaster)) nSpellLvl += 4; + else if(GetHasFeat(FEAT_SPELL_FOCUS_ILLUSION, oCaster)) nSpellLvl += 2; + } + else if(sSave == "Reflex") + { + string sImmunityType = Get2DAString("ai_spells", "ImmunityType", nSpell); + // Give a bonus to our check for half dmg spells unless they can dodge it! + if((sImmunityType == "Fire" || sImmunityType == "Electricity" || sImmunityType == "Acid" || + sImmunityType == "Cold" || sImmunityType == "Sonic") && + !GetHasFeat(FEAT_IMPROVED_EVASION, oCreature)) nSpellLvl += AI_SPELL_CHECK_NO_EVASION_BONUS; + if(AI_DEBUG) ai_Debug("0i_combat", "3050", " nSpellLvl: " + IntToString(nSpellLvl) + + " > nMagic: " + IntToString(GetReflexSavingThrow(oCreature))); + return (nSpellLvl > GetReflexSavingThrow(oCreature)); + } + else if(sSave == "Fortitude") return (nSpellLvl > GetFortitudeSavingThrow(oCreature)); + else if(sSave == "Will") return (nSpellLvl > GetWillSavingThrow(oCreature)); + return TRUE; +} +int ai_GetDragonDC(object oCreature) +{ + int nDC, nHitDice = GetHitDice(oCreature); + if(nHitDice < 4) { nDC = 12; } + else if(nHitDice < 7) { nDC = 13; } + else if(nHitDice < 10) { nDC = 14; } + else if(nHitDice < 13) { nDC = 16; } + else if(nHitDice < 16) { nDC = 18; } + else if(nHitDice < 19) { nDC = 20; } + else if(nHitDice < 22) { nDC = 22; } + else if(nHitDice < 25) { nDC = 24; } + else if(nHitDice < 28) { nDC = 26; } + else if(nHitDice < 31) { nDC = 28; } + else if(nHitDice < 34) { nDC = 30; } + else if(nHitDice < 37) { nDC = 32; } + else if(nHitDice < 39) { nDC = 34; } + else { nDC = 36; } + string sTag = GetTag(oCreature); + if(sTag == "gold_dragon") nDC += 5; + if(sTag == "red_dragon" || sTag == "silver_dragon") return nDC + 4; + else if(sTag == "black_dragon" || sTag == "brass_dragon") return nDC + 3; + else if(sTag == "green_dragon" || sTag == "copper_dragon") return nDC + 2; + else if(sTag == "blue_dragon" || sTag == "bronze_dragon") return nDC + 1; + //else if(sTag == "white_dragon") nDC += 0; + return nDC; +} +void ai_SetCreatureAIScript(object oCreature) +{ + string sCombatAI = GetLocalString(oCreature, AI_DEFAULT_SCRIPT); + // Non-Hostile NPC's do not need to use special tactics by default. + if(sCombatAI == "" && GetLocalInt(GetModule(), AI_RULE_AMBUSH) && d100() < 34) + { + // They should have skill ranks equal to their level + 1 to use a special AI. + int nSkillNeeded = GetHitDice(oCreature) + 1; + /*/ Ambusher: requires either Improved Invisibility or Invisibility. + if(GetHasSpell(SPELL_IMPROVED_INVISIBILITY, oCreature) || + GetHasSpell(SPELL_INVISIBILITY, oCreature)) + { + int bCast = ai_TryToCastSpell(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature); + if(!bCast) bCast = ai_TryToCastSpell(oCreature, SPELL_INVISIBILITY, oCreature); + if(bCast) sCombatAI = "ai_ambusher"; + } */ + if(GetHasFeat(FEAT_SNEAK_ATTACK, oCreature, TRUE)) + { + sCombatAI = "ai_flanker"; + } + // Ambusher: Requires a Hide and Move silently skill equal to your level + 1. + else if(GetSkillRank(SKILL_HIDE, oCreature) >= nSkillNeeded && + GetSkillRank(SKILL_MOVE_SILENTLY, oCreature) >= nSkillNeeded) + { + sCombatAI = "ai_ambusher"; + } + // Defensive : requires Parry skill equal to your level or Expertise. + else if(GetSkillRank(SKILL_PARRY, oCreature) >= nSkillNeeded || + GetHasFeat(FEAT_EXPERTISE, oCreature) || + GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature)) + { + sCombatAI = "ai_defensive"; + } + 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"; + else if(GetSkillRank(SKILL_TAUNT, oCreature) >= nSkillNeeded) sCombatAI = "ai_taunter"; + } + if(sCombatAI == "") + { + int nAssociateType = GetAssociateType(oCreature); + if (nAssociateType == ASSOCIATE_TYPE_FAMILIAR) + { + sCombatAI = "ai_default"; + } + else + { + // Select the best ai for this henchmen based on class. + int nClass = GetClassByPosition(1, oCreature); + // If they have more than one class use the default ai. + if(GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID) sCombatAI = "ai_default"; + else if(nClass == CLASS_TYPE_BARBARIAN) sCombatAI = "ai_barbarian"; + else if(nClass == CLASS_TYPE_BARD) sCombatAI = "ai_bard"; + else if(nClass == CLASS_TYPE_CLERIC) sCombatAI = "ai_cleric"; + else if(nClass == CLASS_TYPE_DRUID) sCombatAI = "ai_druid"; + else if(nClass == CLASS_TYPE_FIGHTER) sCombatAI = "ai_fighter"; + else if(nClass == CLASS_TYPE_MONK) sCombatAI = "ai_monk"; + else if(nClass == CLASS_TYPE_PALADIN) sCombatAI = "ai_paladin"; + else if(nClass == CLASS_TYPE_RANGER) sCombatAI = "ai_ranger"; + else if(nClass == CLASS_TYPE_ROGUE) sCombatAI = "ai_rogue"; + else if(nClass == CLASS_TYPE_SORCERER) sCombatAI = "ai_sorcerer"; + else if(nClass == CLASS_TYPE_WIZARD) sCombatAI = "ai_wizard"; + //else if(nClass == CLASS_TYPE_ABERRATION) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_ANIMAL) sCombatAI = "ai_animal"; + //else if(nClass == CLASS_TYPE_CONSTRUCT) sCombatAI = "ai_animal"; + else if(nClass == CLASS_TYPE_DRAGON) sCombatAI = "ai_dragon"; + //else if(nClass == CLASS_TYPE_ELEMENTAL) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_FEY) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_GIANT) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_HUMANOID) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_MAGICAL_BEAST) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_MONSTROUS) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_OOZE) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_OUTSIDER) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_UNDEAD) sCombatAI = "ai_default"; + //else if(nClass == CLASS_TYPE_VERMIN) sCombatAI = "ai_animal"; + else sCombatAI = "ai_default"; + } + } + if(AI_DEBUG) ai_Debug("0i_combat", "3740", GetName(oCreature) + " is setting AI to " + sCombatAI); + SetLocalString(oCreature, AI_DEFAULT_SCRIPT, sCombatAI); + SetLocalString(oCreature, AI_COMBAT_SCRIPT, sCombatAI); +} +int ai_IsImmuneToSneakAttacks(object oCreature, object oTarget) +{ + if(GetHasFeat(FEAT_UNCANNY_DODGE_2, oTarget) && + GetLevelByClass(CLASS_TYPE_ROGUE, oCreature) + 3 < GetLevelByClass(CLASS_TYPE_ROGUE, oTarget)) return TRUE; + if(GetIsImmune(oTarget, IMMUNITY_TYPE_SNEAK_ATTACK)) return TRUE; + object oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oTarget); + if(ai_GetHasItemProperty(oSkin, ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS, IP_CONST_IMMUNITYMISC_BACKSTAB)) return TRUE; + return FALSE; +} +int ai_IsStrongerThanMe(object oCreature, int nIndex) +{ + int nEnemyCombat = GetLocalInt(oCreature, AI_ENEMY_COMBAT + IntToString(nIndex)); + int nCreatureCombat = ai_GetMyCombatRating(oCreature); + if(AI_DEBUG) ai_Debug("0i_combat", "3955", "IsStrongerThanMe: nCreatureCombat: " + + IntToString(nCreatureCombat) + " nEnemyCombat: " + IntToString(nEnemyCombat)); + return (nEnemyCombat > nCreatureCombat); +} +int ai_StrongOpponent(object oCreature, object oTarget, int nAdj = 2) +{ + int nLevel = GetHitDice(oCreature); + if(AI_DEBUG) ai_Debug("0i_combat", "3220", "ai_StrongOpponent"); + nAdj = nAdj *((nAdj + nLevel) / 10); + if(AI_DEBUG) ai_Debug("0i_combat", "3222", "Is the opponent strong? Target CR >= Our level - nAdj(" + + FloatToString(GetChallengeRating(oTarget), 0, 2) + " >= " + IntToString(nLevel - nAdj) + ")"); + return (FloatToInt(GetChallengeRating(oTarget)) >= nLevel - nAdj); +} +int ai_PowerAttackGood(object oCreature, object oTarget, float fAdj) +{ + int nAvgDmg = ai_GetWeaponDamage(oCreature, 2); + if(AI_DEBUG) ai_Debug("0i_combat", "3412", "PowerAttack: (nAvgDmg: " + IntToString(nAvgDmg) + + " > Target HP: " + IntToString(GetCurrentHitPoints(oTarget)) + + ") Skip: " + IntToString(nAvgDmg > GetCurrentHitPoints(oTarget))); + if(nAvgDmg > GetCurrentHitPoints(oTarget)) return FALSE; + float fAvgDmg = IntToFloat(nAvgDmg); + float fTargetAC = IntToFloat(GetAC(oTarget)); + float fCreatureAtk = IntToFloat(ai_GetCreatureAttackBonus(oCreature)); + float fNormalChance = (21.0 - (fTargetAC - fCreatureAtk)) / 20.0; + // Our chance to hit is already minimum of 5% so this doesn't hurt our chance! + if(fNormalChance <= 0.05) return TRUE; + float fAdjDamage = (fAvgDmg + fAdj) * ((21.0-(fTargetAC - fCreatureAtk + fAdj))/20); + if(AI_DEBUG) ai_Debug("0i_combat", "3420", "fNormalDamage: " + FloatToString(fNormalChance * fAvgDmg, 0, 2) + + " < fAdjDamage: " + FloatToString(fAdjDamage, 0, 2) + " = " + IntToString(fNormalChance * fAvgDmg < fAdjDamage)); + return fNormalChance * fAvgDmg < fAdjDamage; +} +int ai_AttackPenaltyOk(object oCreature, object oTarget, float fAtkAdj) +{ + float fTargetAC = IntToFloat(GetAC(oTarget)); + float fCreatureAtk = IntToFloat(ai_GetCreatureAttackBonus(oCreature)); + float fNormalChance = (21.0-(fTargetAC - fCreatureAtk))/20.0; + if(AI_DEBUG) ai_Debug("0i_combat", "3431", "Normal Avg Chance: " + FloatToString(fNormalChance, 0, 2) + " <= 0.05"); + // We already need a 20 to hit so this doesn't hurt our chances! + if(fNormalChance <= 0.05) return TRUE; + float fAdjChance = (21.0-(fTargetAC - fCreatureAtk + fAtkAdj))/20.0; + if(AI_DEBUG) ai_Debug("0i_combat", "3435", "Adjusted Avg Chance: " + FloatToString(fAdjChance, 0, 2) + " > 0.55"); + // if our chance is 55% or better to hit then use it. + return fAdjChance > 0.55; +} +int ai_AttackBonusGood(object oCreature, object oTarget, float fAtkAdj) +{ + float fTargetAC = IntToFloat(GetAC(oTarget)); + float fCreatureAtk = IntToFloat(ai_GetCreatureAttackBonus(oCreature)); + float fNormalChance = (21.0-(fTargetAC - fCreatureAtk))/20.0; + if(AI_DEBUG) ai_Debug("0i_combat", "3450", "Normal Avg Chance: " + FloatToString(fNormalChance, 0, 2) + " > 0.99"); + // We already hit them with any roll so this will not help. + if(fNormalChance > 0.99) return FALSE; + float fAdjChance = (21.0-(fTargetAC - fCreatureAtk - fAtkAdj))/20.0; + if(AI_DEBUG) ai_Debug("0i_combat", "3454", "Adjusted Avg Chance: " + FloatToString(fAdjChance, 0, 2) + " < 0.0"); + // if our chance increases our to hit then this is good. + return fAdjChance > 0.0; +} +int ai_ACAdjustmentGood(object oCreature, object oTarget, float fACAdj) +{ + float fCreatureAC = IntToFloat(GetAC(oCreature)); + float fTargetAtk = IntToFloat(ai_GetCreatureAttackBonus(oTarget)); + float fNormalChance = (21.0-(fCreatureAC - fTargetAtk))/20.0; + if(AI_DEBUG) ai_Debug("0i_combat", "3444", "Normal Chance To Hit: " + FloatToString(fNormalChance, 0, 2) + " <= 0.05"); + // They already need a 20 to hit so adding more AC is worthless. + if(fNormalChance <= 0.05) return FALSE; + float fAdjChance = (21.0-(fCreatureAC - fTargetAtk + fACAdj))/20.0; + if(AI_DEBUG) ai_Debug("0i_combat", "3448", "Adjusted Chance To Hit: " + FloatToString(fAdjChance, 0, 2) + " < 1.00"); + // Anything less than 1 helps are AC! + return fAdjChance < 1.00; +} +int ai_CanIMoveInCombat(object oCreature) +{ + // DC 15 tumble check is required to not give attacks of opportunity. + return (GetHasFeat(FEAT_MOBILITY, oCreature) || GetHasFeat(FEAT_SPRING_ATTACK, oCreature) || + GetSkillRank(SKILL_TUMBLE, oCreature) > 9); +} +int ai_CanIUseRangedWeapon(object oCreature, int nInMelee) +{ + return (!nInMelee || ai_GetEnemyAttackingMe(oCreature) == OBJECT_INVALID); +} +int ai_CheckRangedCombatPosition(object oCreature, object oTarget, int nAction) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "3559", "Ranged attack: See oTarget? " + + IntToString(GetObjectSeen(oTarget, oCreature)) + " Line of Sight? " + + IntToString(LineOfSightObject(oCreature, oTarget))); + if(nAction == AI_LAST_ACTION_RANGED_ATK) + { + // Watch the nearest enemy instead of our target as they could move toward us. + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + 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(GetHasFeat(FEAT_SNEAK_ATTACK, oCreature)) + { + if(fDistance > AI_RANGE_CLOSE) + { + // 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 [8.0] to " + + GetName(oNearestEnemy) + " to sneak attack 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 are casting a hostile spell then check positioning. + else if(nAction > -1 && Get2DAString("ai_spells", "HostileSetting", nAction) == "1") + { + // We are out of melee and casting a spell on an ally so don't move. + if(GetReputation(oCreature, oTarget) > 89) return FALSE; + float fSpellRange = ai_GetSpellRange(nAction); + float fTargetRange = GetDistanceBetween(oCreature, oTarget); + if(AI_DEBUG) ai_Debug("0i_combat", "3389", "fSpellRange: " + FloatToString(fSpellRange, 0, 2) + + " fTargetRange: " + FloatToString(fTargetRange, 0, 2)); + // Adjust the ranges to see if we are too close. + if(fSpellRange == 5.0) fSpellRange = 4.5f; + //else if(fSpellRange == 8.0) fSpellRange = 8.0f; + else if(fSpellRange > 10.0f) fSpellRange = 10.0f; + if(AI_DEBUG) ai_Debug("0i_combat", "3395", "Adjusted spell range is " + + FloatToString(fSpellRange, 0, 2) + " : " + GetName(oTarget) + " range is " + + FloatToString(fTargetRange, 0, 2) + "."); + // We are closer than we have to be to cast our spell. + if(fTargetRange < fSpellRange) + { + // Lets move back a little, too far and we miss attacks! + if(AI_DEBUG) ai_Debug("0i_combat", "3402", GetName(oCreature) + " is moving away from " + + GetName(oTarget) + "[2.0] to cast a spell."); + ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE); + ActionMoveAwayFromObject(oTarget, FALSE, 2.0); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } +} + return FALSE; +} +int ai_CheckMeleeCombatPosition(object oCreature, object oTarget, int nAction, int nBaseItemType = 0) +{ + // If we are not being attacked then we might want to back out of combat. + if(ai_GetEnemyAttackingMe(oCreature) != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3417", "I am being attacked so stand my ground!"); + return FALSE; + } + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + float fDistance = GetDistanceBetween(oCreature, oNearestEnemy); + if(AI_DEBUG) ai_Debug("0i_combat", "3422", "oNearestEnemy: " + GetName(oNearestEnemy) + " fDistance " + FloatToString(fDistance, 0, 2)); + if(nAction == AI_LAST_ACTION_RANGED_ATK) + { + if(AI_DEBUG) ai_Debug("0i_combat", "3425", GetName(oCreature) + " is moving away from " + GetName(oNearestEnemy) + + "[" + FloatToString(AI_RANGE_MELEE - fDistance + 1.0, 0, 2) + "]" + " to use a ranged weapon."); + ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE); + // Lets move just out of melee range! + int bRun = ai_CanIMoveInCombat(oCreature); + ActionMoveAwayFromObject(oNearestEnemy, bRun, AI_RANGE_MELEE - fDistance + 1.0); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + // If we want to cast a spell this round then back away! + else if(nAction > -1) + { + // Some items we don't need to move on such as wands, staves, and rods. + if(nBaseItemType == BASE_ITEM_ENCHANTED_WAND || + nBaseItemType == BASE_ITEM_MAGICWAND || + nBaseItemType == BASE_ITEM_MAGICSTAFF || + nBaseItemType == BASE_ITEM_MAGICROD) return FALSE; + float fSpellRange = ai_GetSpellRange(nAction); + // A Touch spell means we should not move if we are not the target. + if(fSpellRange <= 5.0 && oCreature != oTarget) return FALSE; + if(AI_DEBUG) ai_Debug("0i_combat", "3446", GetName(oCreature) + " is moving away from " + + GetName(oTarget) + "[" + FloatToString(AI_RANGE_MELEE - fDistance + 1.0, 0, 2) + "] to cast a spell."); + ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE); + SetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST, FALSE); + // Lets move just out of melee range! + int bRun = ai_CanIMoveInCombat(oCreature); + ActionMoveAwayFromObject(oNearestEnemy, bRun, AI_RANGE_MELEE - fDistance + 1.0); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return TRUE; + } + return FALSE; +} +int ai_CheckCombatPosition(object oCreature, object oTarget, int nInMelee, int nAction, int nBaseItemType = 0) +{ + if(AI_DEBUG) ai_Debug("0i_combat", "3460", "|-----> Checking position in combat: " + + GetName(oCreature) + " nMelee: " + IntToString(nInMelee) + + " Action: " + IntToString(nAction) + + " Hold mode: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) + + " Use Advanced Movement: " + IntToString(GetLocalInt(GetModule(), AI_RULE_ADVANCED_MOVEMENT))); + // We don't want to move around in combat if we were told to hold. + if(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) return FALSE; + if(!GetLocalInt(GetModule(), AI_RULE_ADVANCED_MOVEMENT)) return FALSE; + if(ai_CompareLastAction(oCreature, AI_LAST_ACTION_MOVE)) return FALSE; + // We are not in melee combat so lets see how close we should get. + if(!nInMelee) return ai_CheckRangedCombatPosition(oCreature, oTarget, nAction); + // If we are in melee we might need to move out of combat. + return ai_CheckMeleeCombatPosition(oCreature, oTarget, nAction, nBaseItemType); +} diff --git a/_module/nss/0i_constants.nss b/_module/nss/0i_constants.nss new file mode 100644 index 00000000..bb7dbe82 --- /dev/null +++ b/_module/nss/0i_constants.nss @@ -0,0 +1,667 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Name: 0i_constants +// Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Include script for handling all constants for the ai. + These constants are static and can only be changed in the toolset. + Changes to any constants will not take effect until the scripts are recompiled. +*/////////////////////////////////////////////////////////////////////////////// + +const string PHILOS_VERSION = "Philos' Enhancing Player System (PEPS) version:07.20.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 +// so the ai_SetAIRules() will rewrite the new server rule values. +// File Name: peps_database.sqlite3 +//********************************** SERVER *********************************** +// Turn On/Off Debug. You can only use the debug with the pi_debug/pe_debug scripts. +// 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; +// 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. +const int AI_TAUNT_COOLDOWN = 3; +// Animal Empathy cool down time before the AI attemps another check. +const int AI_EMPATHY_COOLDOWN = 3; +// Arcane Spell failure% or less than, for a caster to still try to cast a spell. +const int AI_ASF_WILL_USE = 15; +// Monsters chance to heal while in combat per round. +const int AI_MONSTER_HEAL_IN_COMBAT_CHANCE = 70; +// Monsters chance to heal when out of combat per heart beat. +const int AI_MONSTER_HEAL_OUT_COMBAT_CHANCE = 70; +// Allows Henchman to have a widget if using the henchman AI. +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 cast summons spells when prebuffing. +const int AI_PRESUMMONS = TRUE; +// Allows monsters to use tactical AI scripts such as ambush, flanker, ranged. +const int AI_TACTICAL = TRUE; +// Enemies may summon familiars and Animal companions and will be randomized. +const int AI_SUMMON_COMPANIONS = FALSE; +// Allow the AI to move during combat base on the situation and action taking. +const int AI_ADVANCED_MOVEMENT = TRUE; +// Follow Item Level Restrictions for AI. +const int AI_ITEM_LEVEL_RESTRICTIONS = FALSE; +// Allow the AI to use Use Magic Device. +const int AI_USE_MAGIC_DEVICE = TRUE; +// Allow the AI to use healing kits. +const int AI_HEALING_KITS = TRUE; +// Associates are permanent and don't get removed when the master dies. +const int AI_COMPANIONS_PERMANENT = FALSE; +// Monster AI's chance (0 to 100) to attack the weakest target instead of the nearest. +// The higher the number the harder the encounter with monsters! +const int AI_TARGET_WEAKEST = 0; +// Variable that can change the distance creatures will come and attack after +// hearing a shout from an ally that sees or hears an enemy. +// Or when searching for an invisible, heard enemy. +// 10.0 Short, 30.0 Average, 40.0 Long, 60.0 Huge. +const float AI_SEARCH_DISTANCE = 30.0; +// Enemy corpses remain on the floor instead of dissappearing. +const int AI_CORPSE_REMAIN = FALSE; +// Monsters will wander around when not in combat. +const int AI_WANDER = FALSE; +// Variable distance monsters can wander away from their spawn point. +const float AI_WANDER_DISTANCE = 0.0; +// Variable that allows monsters to open doors when wandering around out of combat. +const int AI_OPEN_DOORS = FALSE; +// Monster's actual perception distance. +// 8 Short(10 sight/listen) 9 Medium(20 sight/listen) 10 Long(35 sight/20 listen) +// 11 Default(Based on appearance.2da Most creatures use 9, bosses use 10). +const int AI_MONSTER_PERCEPTION = 11; +// Should the AI auto adjust the XP scale to remove party size penalty? +const int AI_PARTY_SCALE = FALSE; + +//**************************** DM Based Constants **************************** +// The constant the server wants set to allow players to use specific widgets buttons. +// 0 Allows all buttons. See ASSOCIATE_WIDGET_BUTTONS below for values needed to be +// added to block those buttons. +// Example: BTN_CMD_GHOST_MODE = 0x00000800; To remove you would put 2048 below. +// Since Hex 800 is Decimal 2048. +const int AI_DM_WIDGET_ACCESS_BUTTONS = 0; +// The constant the server wants set to allow players to use specific AI buttons. +// 0 Allows all buttons. See ASSOCIATE_AI_BUTTONS below for values needed to be +// added to block those buttons. +// Example: BTN_AI_MAGIC_LEVEL = 0x00000040; To remove you would put 64 below. +// Since Hex 40 is Decimal 64. Adding BTN_AI_LOOT = 0x00001000; to that would be +// 64 + 4096 = 4160 to Block Magic Level and Auto Looting. +const int AI_DM_AI_ACCESS_BUTTONS = 0; +//************************** CONVERSATION CONSTANTS ************************** +// Player's can tell their associates to ignore enemy associates. +const int AI_IGNORE_ASSOCIATES_ON = TRUE; +// Associates with a Taunt skill higher than their level can be told to taunt. +const int AI_TAUNTING_ON = TRUE; +// Associates that cast spells can be told to use counterspell. +const int AI_COUNTERSPELLING_ON = TRUE; +// Associates with lore skill higher than the master can identify items. +const int AI_IDENTIFY_ON = TRUE; +// Associates can be called upon to scout ahead for monsters. +const int AI_SCOUT_AHEAD_ON = TRUE; +// A player can open a henchmen's inventory. +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. +const int AI_REMOVE_HENCHMAN_ON = FALSE; +//***************************** Health Constants ***************************** +// % of health for when a creature is considered wounded. +const int AI_HEALTH_WOUNDED = 50; +// % of health when creature is considered badly wounded. +const int AI_HEALTH_BLOODY = 25; +//***************************** MORAL CONSTANTS ****************************** +// Moral checks are only made once a creature is below AI_HEALTH_WOUNDED. +// The moral DC is AI_MORAL_DC - the number of Allies. Default: 5 +const int AI_WOUNDED_MORAL_DC = 5; +// Once a creature goes below AI_HEALTHY_BLOODY then it uses this moral DC. Default: 15 +const int AI_BLOODY_MORAL_DC = 15; +//******************************* WINDOW CONSTANTS ***************************** +const string AI_MAIN_NUI = "ai_main_nui"; +const string AI_COMMAND_NUI = "_command_nui"; +const string AI_NUI = "_ai_nui"; +const string AI_WIDGET_NUI = "_widget_nui"; +const string AI_LOOTFILTER_NUI = "_lootfilter_nui"; +const string AI_COPY_NUI = "_copy_nui"; +const string AI_PLUGIN_NUI = "ai_plugin_nui"; +const string AI_QUICK_WIDGET_NUI = "_quick_widget_nui"; +const string AI_SPELL_MEMORIZE_NUI = "_spell_memorize_nui"; +const string AI_SPELL_KNOWN_NUI = "_spell_known_nui"; +const string AI_SPELL_DESCRIPTION_NUI = "ai_spell_desc_nui"; +const string AI_EFFECT_ICON_NUI = "ai_effect_icon_nui"; +//******************************* CORE CONSTANTS ******************************* +// 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. +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"; +// Variable used to let PEPS know that a monster plugin changed the monster. +const string AI_MONSTER_CHANGED = "AI_MONSTER_CHANGED"; +// Variable used to save an associates class list to change known list json. +const string AI_CLASS_LIST_JSON = "AI_CLASS_LIST_JSON"; +// Startup variable to tell plugins that we have started. +const string AI_STARTING_UP = "AI_STARTING_UP"; +// Add plugin variable to tell plugins that we are adding them to PEPS. +const string AI_ADD_PLUGIN = "AI_ADD_PLUGIN"; +// Startup variable to tell plugins what json array to add their plugin to. +const string AI_JSON_PLUGINS = "AI_JSON_PLUGINS"; +// Plugin variable to have plugins return if they setup the plugin in the json for PEPS. +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; +// 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. +const string AI_NO_ANIMATION = "AI_NO_ANIMATION"; +// How many seconds in a combat round. +const int AI_COMBAT_ROUND_IN_SECONDS = 6; +// Used for actions that take x seconds but don't have an action constant. +const string AI_COMBAT_WAIT_IN_SECONDS = "AI_COMBAT_WAIT_IN_SECONDS"; +// Constants used to define the difficulty of the battle for associates. +// 20+ : Impossible - Cannot win. +// 17 to 19 : Overpowering - Use all of our powers. +// 15 to 16 : Very Difficult - Use all of our power (Highest level spells). +// 11 to 14 : Challenging - Use most of our power (Higher level powers). +// 8 to 10 : Moderate - Use half of our power (Mid level powers and less). +// 5 to 7 : Easy - Use our weaker powers (Lowest level powers). +// 2 to 4 : Effortless - Don't waste spells and powers on this. +// 1 or less: Pointless - We probably should ignore these dangers. +const int AI_COMBAT_IMPOSSIBLE = 21; +const int AI_COMBAT_OVERPOWERING = 17; +const int AI_COMBAT_VERY_DIFFICULT = 15; +const int AI_COMBAT_CHALLENGING = 11; +const int AI_COMBAT_MODERATE = 10; +const int AI_COMBAT_EASY = 7; +const int AI_COMBAT_EFFORTLESS = 4; +// Variables used to keep track of enemies in combat. +const string AI_ENEMY = "AI_ENEMY"; // The enemy objects. +const string AI_ENEMY_DISABLED = "AI_ENEMY_DISABLED"; // Int if they are disabled. +const string AI_ENEMY_PERCEIVED = "AI_ENEMY_PERCEIVED"; // TRUE if we have seen or heard them, FALSE if not. +const string AI_ENEMY_RANGE = "AI_ENEMY_RANGE"; // The range from OBJECT_SELF. +const string AI_ENEMY_COMBAT = "AI_ENEMY_COMBAT"; // Combat rating: (BAB + AC - 10) / 2 +const string AI_ENEMY_MELEE = "AI_ENEMY_MELEE"; // Enemies within 5 meters - Allies within 5 meters. +const string AI_ENEMY_HEALTH = "AI_ENEMY_HEALTH"; // % of hitpoints. +const string AI_ENEMY_NUMBERS = "AI_ENEMY_NUM"; // Number of enemies in combat. +const string AI_ENEMY_POWER = "AI_ENEMY_POWER"; // (Level * Health %) / 100 added for each enemy to this. +const string AI_ENEMY_NEAREST = "AI_ENEMY_NEAREST"; // Nearest enemy to OBJECT_SELF. +// Variables used to keep track of allies in combat. +const string AI_ALLY = "AI_ALLY"; // All friendly creatures +const string AI_ALLY_DISABLED = "AI_ALLY_DISABLED"; // Int if they are disabled. +const string AI_ALLY_PERCEIVED = "AI_ALLY_PERCEIVED"; // All allies are set to be seen and heard. +const string AI_ALLY_RANGE = "AI_ALLY_RANGE"; // The range from OBJECT_SELF. +const string AI_ALLY_COMBAT = "AI_ALLY_COMBAT"; // Combat rating: (BAB + AC - 10) / 2 +const string AI_ALLY_MELEE = "AI_ALLY_MELEE"; // Enemies within 5 meters - Allies within 5 meters. +const string AI_ALLY_HEALTH = "AI_ALLY_HEALTH"; // % of hitpoints. +const string AI_ALLY_NUMBERS = "AI_ALLY_NUM"; // Number of allies in combat. +const string AI_ALLY_POWER = "AI_ALLY_POWER"; // (Level * Health %) / 100 added for each enemy to this. +// Variable name used to define the ai scripts being used by creatures. +const string AI_DEFAULT_SCRIPT = "AI_DEFAULT_SCRIPT"; +const string AI_COMBAT_SCRIPT = "AI_COMBAT_SCRIPT"; +// Constants used in a creatures listening patterns. +const string AI_I_SEE_AN_ENEMY = "AI_I_SEE_AN_ENEMY"; +const string AI_I_HEARD_AN_ENEMY = "AI_I_HEARD_AN_ENEMY"; +const string AI_ATKED_BY_WEAPON = "AI_ATK_BY_WEAPON"; +const string AI_ATKED_BY_SPELL = "AI_ATK_BY_SPELL"; +const string AI_I_AM_WOUNDED = "AI_I_AM_WOUNDED"; +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; +const int AI_ALLY_IS_WOUNDED = 5; +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 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"; +// Used to keep track of oCreature attempting to hide. +const string AI_TRIED_TO_HIDE = "AI_TRIED_TO_HIDE"; +// Constant used by creatures to keep track of invisible creatures. +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"; +const int AI_LAST_ACTION_CAST_SPELL = -1; +const int AI_LAST_ACTION_NONE = -2; +const int AI_LAST_ACTION_MELEE_ATK = -3; +const int AI_LAST_ACTION_RANGED_ATK = -4; +const int AI_LAST_ACTION_USED_FEAT = -5; +const int AI_LAST_ACTION_USED_ITEM = -6; +const int AI_LAST_ACTION_USED_SKILL = -7; +const int AI_LAST_ACTION_MOVE = -8; +// Variable name used to keep track of Action Modes. +const string AI_CURRENT_ACTION_MODE = "AI_CURRENT_ACTION_MODE"; +// Variable name used to keep track of object usage by the AI. +const string AI_OBJECT_IN_USE = "AI_OBJECT_IN_USE"; +// Variable name used to keep a creatures attacked targets. +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 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. +const string AI_ROUND = "AI_ROUND"; +// Combat Ranges +const float AI_RANGE_MELEE = 5.0f; // Anyone within this is considered to be in melee. +const float AI_RANGE_CLOSE = 8.0f; // For anything requiring to be within 30'. +const float AI_RANGE_LONG = 15.0f; // Mainly used for distance ranged attacks. +const float AI_RANGE_PERCEPTION = 35.0f; // This is the distance for perception in battle. +const float AI_RANGE_BATTLEFIELD = 40.0f; // This is the size of the battlefield area. +// Spell ranges. +const float AI_SHORT_DISTANCE = 8.0f; +const float AI_MEDIUM_DISTANCE = 20.0f; +const float AI_LONG_DISTANCE = 40.0f; +// When computer checks if a creature should cast a specific spell at a target. +// Computer makes a spell check vs the targets saving throw. +// Spell check roll for the caster is +// [Innate spell Level + Random (AI_SPELL_CHECK_DIE) + AI_SPELL_CHECK_BONUS] +// If the spell gives a save for half (i.e. FireBall) and the target does not have +// Evasion then they get an additional bonus of AI_SPELL_CHECK_NO_EVASION_BONUS. +const int AI_SPELL_CHECK_DIE = 6; +const int AI_SPELL_CHECK_BONUS = 3; +const int AI_SPELL_CHECK_NO_EVASION_BONUS = 10; +// When the computer checks if a creature should use defensive casting it looks +// at the spell level + AI_DEFENSIVE_CASTING_DC vs casters concentration +// and feat bonuses (i.e. COMBAT_CASTING) + Random (AI_DEFENSIVE_CASTING_ROLL). +const int AI_DEFENSIVE_CASTING_DC = 19; // 19 will allow them to use it at 50% effectiveness. +const int AI_DEFENSIVE_CASTING_DIE = 10; +// When the computer checks to see if it should cast in melee combat it looks +// at CASTING_IN_MELEE_DC + SpellLevel + (Num of creatures in melee * GetHitDice (NearestEnemy)); +// vs the casters concentration + Random (AI_CASTING_IN_MELEE_ROLL). +const int AI_CASTING_IN_MELEE_DC = 10; +const int AI_CASTING_IN_MELEE_ROLL = 10; +// For getting a specific class the following constants were added to flesh out +// the CLASS_TYPE_* +const int AI_CLASS_TYPE_CASTER = -1; +const int AI_CLASS_TYPE_DIVINE = -2; +const int AI_CLASS_TYPE_ARCANE = -3; +const int AI_CLASS_TYPE_WARRIOR = -4; +// For getting a specific race the following constants were added to flesh out +// the RACIAL_TYPE_* +const int AI_RACIAL_TYPE_ANIMAL_BEAST = -1; +const int AI_RACIAL_TYPE_HUMANOID = -2; +// Bitwise constants for negative conditions we might want to try to cure +const int AI_CONDITION_POISON = 0x00000001; +const int AI_CONDITION_DISEASE = 0x00000002; +const int AI_CONDITION_BLINDDEAF = 0x00000004; +const int AI_CONDITION_ATK_DECREASE = 0x00000008; +const int AI_CONDITION_DMG_DECREASE = 0x00000010; +const int AI_CONDITION_DMG_I_DECREASE = 0x00000020; +const int AI_CONDITION_SKILL_DECREASE = 0x00000040; +const int AI_CONDITION_SAVE_DECREASE = 0x00000080; +const int AI_CONDITION_SR_DECREASE = 0x00000100; +const int AI_CONDITION_AC_DECREASE = 0x00000200; +const int AI_CONDITION_SLOW = 0x00000400; +const int AI_CONDITION_ABILITY_DRAIN = 0x00000800; +const int AI_CONDITION_LEVEL_DRAIN = 0x00001000; +const int AI_CONDITION_CHARMED = 0x00002000; +const int AI_CONDITION_DAZED = 0x00004000; +const int AI_CONDITION_STUNNED = 0x00008000; +const int AI_CONDITION_FRIGHTENED = 0x00010000; +const int AI_CONDITION_CONFUSED = 0x00020000; +const int AI_CONDITION_CURSE = 0x00040000; +const int AI_CONDITION_PARALYZE = 0x00080000; +const int AI_CONDITION_DOMINATED = 0x00100000; +// 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(). +const string sAIModeVarname = "ASSOCIATE_MODES"; +//const int AI_MODE_DISTANCE_CLOSE = 0x00000001; // Stays within AI_DISTANCE_CLOSE of master. +//const int AI_MODE_DISTANCE_MEDIUM = 0x00000002; // Stays within AI_DISTANCE_MEDIUM of master. +const int AI_MODE_ACTION_GHOST = 0x00000004; // Defines if the player is using Ghost mode when using associate actions. +const int AI_MODE_SELF_HEALING_OFF = 0x00000008; // Creature will not use healing items or spells on self. +const int AI_MODE_PARTY_HEALING_OFF = 0x00000010; // Creature will not use healing items or spells on party. +const int AI_MODE_GHOST = 0x00000020; // Creature can move through other creatures. +const int AI_MODE_OPEN_DOORS = 0x00000040; // Creature will attempted to open all doors. +const int AI_MODE_EQUIP_WEAPON_OFF = 0x00000080; // The AI will not equip weapons. +const int AI_MODE_BASH_LOCKS = 0x00000100; // Will bash locks if cannot open door/placeable. +const int AI_MODE_AGGRESSIVE_SEARCH = 0x00000200; // Sets associate to continuous search mode. +const int AI_MODE_AGGRESSIVE_STEALTH = 0x00000400; // Sets associate to continuous stealth mode. +const int AI_MODE_PICK_LOCKS = 0x00000800; // Will pick locks if possible. +const int AI_MODE_DISARM_TRAPS = 0x00001000; // Will disarm traps. +const int AI_MODE_SCOUT_AHEAD = 0x00002000; // Will move ahead of master and scout. +const int AI_MODE_DEFEND_MASTER = 0x00004000; // Will attack enemies attacking our master. +const int AI_MODE_STAND_GROUND = 0x00008000; // Will stay in one place until new command. +const int AI_MODE_STOP_RANGED = 0x00010000; // Will not use ranged weapons. +const int AI_MODE_FOLLOW = 0x00020000; // Keeps associate following master ignoring combat. +const int AI_MODE_PICKUP_ITEMS = 0x00040000; // Will pickup up all items for master. +const int AI_MODE_COMMANDED = 0x00080000; // In Command mode then don't follow, search, etc. +const int AI_MODE_IGNORE_TRAPS = 0x00100000; // Creature will ignore traps on the floor. +const int AI_MODE_NO_STEALTH = 0x00200000; // Will not cast invisibilty or use stealth. +const int AI_MODE_DO_NOT_SPEAK = 0x00400000; // Tells the henchmen to be silent and not talk. +const int AI_MODE_CHECK_ATTACK = 0x00800000; // Will only engage in combats they think they can win. +const int AI_MODE_IGNORE_ASSOCIATES = 0x01000000; // Will ignore associates in combat. +//const int AI_MODE_ = 0x02000000; // Not used. +//const int AI_MODE_ = 0x04000000; // Not used. +//const int AI_MODE_ = 0x08000000; // Not used. +//const int AI_MODE_ = 0x10000000; // Not used. +//const int AI_MODE_ = 0x20000000; // Not used. +//const int AI_MODE_ = 0x40000000; // Not used. +//const int AI_MODE_ = 0x80000000; // Not used. +// Bitwise constants for Associate magic modes that are used with Get/SetAssociateMagicMode(). +const string sMagicModeVarname = "ASSOCIATE_MAGIC_MODES"; +const int AI_MAGIC_BUFF_MASTER = 0x00000001; // Buffs master before other allies. +const int AI_MAGIC_NO_MAGIC = 0x00000002; // Will not use any magic (Spells, abilities). +const int AI_MAGIC_DEFENSIVE_CASTING = 0x00000004; // Will only cast defensive spells. +const int AI_MAGIC_OFFENSIVE_CASTING = 0x00000008; // Will only cast offensive spells. +const int AI_MAGIC_STOP_DISPEL = 0x00000010; // Will not cast dispel type spells. +const int AI_MAGIC_BUFF_AFTER_REST = 0x00000020; // Will buff the party after resting. +const int AI_MAGIC_NO_MAGIC_ITEMS = 0x00000040; // Will not use magic items in combat. +const int AI_MAGIC_CURE_SPELLS_OFF = 0x00000080; // Will not cast cure spells. +const int AI_MAGIC_EFFECT_ICON_REPORT = 0x00000100; // Sets each player to report Effect Icons to chat. +//const int = 0x00000200; // Not used. +//const int = 0x00000400; // Not used. +const int AI_MAGIC_NO_SPONTANEOUS_CURE = 0x00000800; // Caster will stop using spontaneous cure spells. +//const int AI_MAGIC_ = 0x00001000; // Not used. +//const int AI_MAGIC_ = 0x00002000; // Not used. +//const int AI_MAGIC_ = 0x00004000; // Not used. +//const int AI_MAGIC_ = 0x00008000; // Not used. +//const int AI_MAGIC_ = 0x00010000; // Not used. +//const int AI_MAGIC_ = 0x00020000; // Not used. +//const int AI_MAGIC_ = 0x00040000; // Not used. +//const int AI_MAGIC_ = 0x00080000; // Not used. +//const int AI_MAGIC_ = 0x00100000; // Not used. +//const int AI_MAGIC_ = 0x00200000; // Not used. +//const int AI_MAGIC_ = 0x00400000; // Not used. +//const int AI_MAGIC_ = 0x00800000; // Not used. +//const int AI_MAGIC_ = 0x01000000; // Not used. +//const int AI_MAGIC_ = 0x02000000; // Not used. +//const int AI_MAGIC_ = 0x04000000; // Not used. +//const int AI_MAGIC_ = 0x08000000; // Not used. +//const int AI_MAGIC_ = 0x10000000; // Not used. +//const int AI_MAGIC_ = 0x20000000; // Not used. +//const int AI_MAGIC_ = 0x40000000; // Not used. +//const int AI_MAGIC_ = 0x80000000; // Not used. +// Use by NUI windows to stop saving move states while loading. +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. +// 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. +const int BTN_AI_USE_RANGED = 0x00000002; // AI uses ranged attacks. +const int BTN_AI_USE_SEARCH = 0x00000004; // AI uses Search. +const int BTN_AI_USE_STEALTH = 0x00000008; // AI uses Stealth. +const int BTN_AI_REMOVE_TRAPS = 0x00000010; // AI seeks out and removes traps. +const int BTN_AI_PICK_LOCKS = 0x00000020; // AI will attempt to pick locks. +const int BTN_AI_MAGIC_LEVEL = 0x00000040; // Increase chance to use magic in battle. +const int BTN_AI_NO_SPONTANEOUS = 0x00000080; // Stops the use of spontaneous spells. +const int BTN_AI_NO_MAGIC_USE = 0x00000100; // Will not use magic in battle. +const int BTN_AI_NO_MAGIC_ITEM_USE = 0x00000200; // Will not use magic items in battle. +const int BTN_AI_DEF_MAGIC_USE = 0x00000400; // Will use Defensive spells only in battle. +const int BTN_AI_OFF_MAGIC_USE = 0x00000800; // Will use Offensive spells only in battle. +const int BTN_AI_LOOT = 0x00001000; // Auto picking up loot on/off. +const int BTN_AI_FOLLOW_TARGET = 0x00002000; // Selects a target to follow. +const int BTN_AI_HEAL_OUT = 0x00004000; // Increase minimum hp required before ai heals out of combat. +const int BTN_AI_PERC_RANGE = 0x00008000; // Adjust the perception range of the henchman. +const int BTN_AI_HEAL_IN = 0x00010000; // Increase minimum hp required before ai heals in combat. +const int BTN_AI_OPEN_DOORS = 0x00020000; // AI will open all closed doors. +const int BTN_AI_STOP_SELF_HEALING = 0x00040000; // Stops AI from using any healing on self. +const int BTN_AI_STOP_PARTY_HEALING = 0x00080000; // Stops AI from using any healing on party. +const int BTN_AI_IGNORE_ASSOCIATES = 0x00100000; // AI will deprioritize enemy associates. +const int BTN_AI_STOP_CURE_SPELLS = 0x00200000; // AI uses cure spells. +const int BTN_AI_STOP_WEAPON_EQUIP = 0x00400000; // AI can equip different weapons. +const int BTN_AI_IGNORE_TRAPS = 0x00800000; // AI will ignore traps on the floor. +//const int BTN_AI = 0x01000000; // Not used. +//const int BTN_AI = 0x02000000; // Not used. +const int BTN_AI_BASH_LOCKS = 0x04000000; // AI will attempt to bash any locks they can't get past. +const int BTN_AI_REDUCE_SPEECH = 0x08000000; // Reduce the associates speaking. +// Bitwise menu constants for DM access for players Widget buttons uses BTN_CMD and BTN_BUFF bitwise see above. +const string sDMWidgetAccessVarname = "AI_RULES_WIDGET_BUTTONS_ACCESS"; +// Bitwise menu constants for DM access for players AI buttons uses BTN_AI bitwise see above. +const string sDMAIAccessVarname = "AI_RULES_AI_BUTTONS_ACCESS"; +// Variable name for DM widget buttons. +const string sDMWidgetButtonVarname = "DM_WIDGET_BUTTONS"; +// DM Widget buttons states. +const int BTN_DM_WIDGET_OFF = 0x00000001; // Removes the widget from the screen, For PC it removes all associates. +const int BTN_DM_WIDGET_LOCK = 0x00000002; // Locks the widget to the current coordinates. +const int BTN_DM_CMD_GROUP1 = 0x00000004; // Does all the group 1 commands. +const int BTN_DM_CMD_GROUP2 = 0x00000008; // Does all the group 2 commands. +const int BTN_DM_CMD_GROUP3 = 0x00000010; // Does all the group 3 commands. +const int BTN_DM_CMD_GROUP4 = 0x00000020; // Does all the group 4 commands. +const int BTN_DM_CMD_GROUP5 = 0x00000040; // Does all the group 5 commands. +const int BTN_DM_CMD_GROUP6 = 0x00000080; // Does all the group 6 commands. +const int BTN_DM_CMD_CAMERA = 0x00000100; // Selects new object to hold the camera view. +const int BTN_DM_CMD_INVENTORY = 0x00000200; // Selects a creature to open the inventory of. +const int BTN_DM_CMD_MEMORIZE = 0x00000400; // Allows associate to change memorized spells. +// Bitwise constants for Associate loot options that are used with Get/SetAssociateLootMode(). +const string sLootFilterVarname = "ASSOCIATE_LOOT_MODES"; +const int AI_LOOT_PLOT = 0x00000001; +const int AI_LOOT_WEAPONS = 0x00000002; +const int AI_LOOT_ARMOR = 0x00000004; +const int AI_LOOT_SHIELDS = 0x00000008; +const int AI_LOOT_HEADGEAR = 0x00000010; +const int AI_LOOT_BELTS = 0x00000020; +const int AI_LOOT_BOOTS = 0x00000040; +const int AI_LOOT_CLOAKS = 0x00000080; +const int AI_LOOT_GLOVES = 0x00000100; +const int AI_LOOT_JEWELRY = 0x00000200; +const int AI_LOOT_POTIONS = 0x00000400; +const int AI_LOOT_SCROLLS = 0x00000800; +const int AI_LOOT_WANDS_RODS_STAVES = 0x00001000; +const int AI_LOOT_GEMS = 0x00002000; +const int AI_LOOT_MISC = 0x00004000; +const int AI_LOOT_ARROWS = 0x00008000; +const int AI_LOOT_BOLTS = 0x00010000; +const int AI_LOOT_BULLETS = 0x00020000; +const int AI_LOOT_GIVE_TO_PC = 0x80000000; +// Default value for all loot filters to be on. +const int AI_LOOT_ALL_ON = 262143; +// Variable to keep track of who is in ghost mode. +const string sGhostModeVarname = "AI_GHOST_MODE_ON"; +// Variables for gold piece value to pickup items. +const string AI_MIN_GOLD_ = "AI_MIN_GOLD_"; +// Variable used to limit the spamming of NUI buttons. +const string AI_DELAY_NUI_USE = "AI_DELAY_NUI_USE"; +// Variable for maximum weight to pickup from looting. +const string AI_MAX_LOOT_WEIGHT = "AI_MAX_LOOT_WEIGHT"; +// Variable to change the size of the widget buttons. +const string AI_WIDGET_BUTTON_SIZE = "AI_WIDGET_BUTTON_SIZE"; +// Variable to change the difficulty so a player can adjust spell usage. +const string AI_DIFFICULTY_ADJUSTMENT = "AI_DIFFICULTY_ADJUSTMENT"; +// Variable to change the Healing % limit for out of combat. +const string AI_HEAL_OUT_OF_COMBAT_LIMIT = "AI_HEAL_OUT_OF_COMBAT_LIMIT"; +// Variable to change the Healing % limit for in combat. +const string AI_HEAL_IN_COMBAT_LIMIT = "AI_HEAL_IN_COMBAT_LIMIT"; +// Variable to change the looting range. +const string AI_LOOT_CHECK_RANGE = "AI_LOOT_CHECK_RANGE"; +// Variable to change the lock checking range. +const string AI_LOCK_CHECK_RANGE = "AI_LOCK_CHECK_RANGE"; +// Variable to change the trap checking range. +const string AI_TRAP_CHECK_RANGE = "AI_TRAP_CHECK_RANGE"; +// Variable to change the range an associate follows the pc. +const string AI_FOLLOW_RANGE = "AI_FOLLOW_RANGE"; +// Variable that holds the target for an associate to follow. +const string AI_FOLLOW_TARGET = "AI_FOLLOW_TARGET"; +// Variable that holds the perception range of associates i.e. 8, 9, 10, 11. +const string AI_ASSOCIATE_PERCEPTION = "AI_PERCEPTION_RANGE"; +// Variable that holds the perception distance of associates i.e. 30.0 meters. +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 Spell widgets json data. +const string AI_SPELLS_WIDGET = "AI_SPELLS_WIDGET"; +// The number of Buff Groups +const int AI_BUFF_GROUPS = -17; +// Variable name used to keep track if we have set our talents. +const string AI_TALENTS_SET = "AI_TALENTS_SET"; +// New talent categories +const string AI_TALENT_ENHANCEMENT = "E"; +const string AI_TALENT_PROTECTION = "P"; +const string AI_TALENT_SUMMON = "S"; +const string AI_TALENT_HEALING = "H"; +const string AI_TALENT_CURE = "C"; +const string AI_TALENT_INDISCRIMINANT_AOE = "I"; +const string AI_TALENT_DISCRIMINANT_AOE = "D"; +const string AI_TALENT_RANGED = "R"; +const string AI_TALENT_TOUCH = "T"; +// Talent types. +const int AI_TALENT_TYPE_SPELL = 1; +const int AI_TALENT_TYPE_SP_ABILITY = 2; +const int AI_TALENT_TYPE_FEAT = 3; +const int AI_TALENT_TYPE_ITEM = 4; +// Variable name used to have associates fight the pc's selected target. +const string AI_PC_LOCKED_TARGET = "AI_PC_LOCKED_TARGET"; +// Variable name of json talent immunity. +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_"; +// Backward compatability constants. +const int X2_EVENT_CONCENTRATION_BROKEN = 12400; +// Variable set on the module if the module is using PRC. +const string AI_USING_PRC = "AI_USING_PRC"; +// Variable that sets if the rules have been added to the module. +const string AI_RULES_SET = "AI_RULES_SET"; +// Variable that tells us that oCreature has run our OnSpawn event. +const string AI_ONSPAWN_EVENT = "AI_ONSPAWN_EVENT"; +// Variable used to define a creatures unique Tag for widgets. +const string AI_TAG = "AI_TAG"; +// Variable that saves any module target event script so we can pass it along. +const string AI_MODULE_TARGET_EVENT = "AI_MODULE_TARGET_EVENT"; +// Variable for plugins to inject Targeting mode code into PEPS. +const string AI_PLUGIN_TARGET_SCRIPT = "AI_PLUGIN_TARGET_SCRIPT"; +// Variable for PEPS to inject effect icons NUI information. +const string AI_MODULE_GUI_EVENT = "AI_MODULE_GUI_EVENT"; +// Variable used on the player to define the targeting action in the OnPlayerTarget event script. +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"; +// 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(). +const string sIPResistVarname = "AI_IP_RESIST"; +// Variable name for the Int constant for reduced damage item property set to the bonus of the weapon required. +const string sIPReducedVarname = "AI_IP_REDUCED"; +// Variable name for the Int (Bool) constant for the haste item property. +const string sIPHasHasteVarname = "AI_IP_HAS_HASTE"; +// Variable name used to hold the party xp base needed to adjust party xp. +const string AI_BASE_PARTY_SCALE_XP = "AI_BASE_PARTY_SCALE_XP"; +//***************************** AI RULES CONSTANTS ***************************** +// Variable name set to a creatures full name to set debugging on. +const string AI_RULE_DEBUG_CREATURE = "AI_RULE_DEBUG_CREATURE"; +// Moral checks on or off. +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 use the ambush AI scripts. +const string AI_RULE_AMBUSH = "AI_RULE_AMBUSH"; +// Enemies may summon familiars and Animal companions and will be randomized. +const string AI_RULE_SUMMON_COMPANIONS = "AI_RULE_SUMMON_COMPANIONS"; +// Allows monsters cast summons spells when prebuffing. +const string AI_RULE_PRESUMMON = "AI_RULE_PRESUMMON"; +// Allow the AI move during combat base on the situation and action taking. +const string AI_RULE_ADVANCED_MOVEMENT = "AI_RULE_ADVANCED_MOVEMENT"; +// Follow Item Level Restrictions for monsters/associates. +// Usually off in Single player and on in Multi player. +const string AI_RULE_ILR = "AI_RULE_ILR"; +// Allow the AI to use Use Magic Device. +const string AI_RULE_ALLOW_UMD = "AI_RULE_ALLOW_UMD"; +// Allow the AI to use healing kits. +const string AI_RULE_HEALERSKITS = "AI_RULE_HEALERSKITS"; +// Summoned associates are permanent and don't disappear when the caster dies. +const string AI_RULE_PERM_ASSOC = "AI_RULE_PERM_ASSOC"; +// Monster AI's chance to attack the weakest target instead of the nearest. +const string AI_RULE_AI_DIFFICULTY = "AI_RULE_AI_DIFFICULTY"; +// Variable that can change the distance creatures will come and attack after +// hearing a shout from an ally that sees or hears an enemy. +// Or when searching for an invisible, heard enemy. +// 10.0 Short, 30.0 Average, 40.0 Long, 60.0 Huge. +const string AI_RULE_PERCEPTION_DISTANCE = "AI_RULE_PERCEPTION_DISTANCE"; +// Enemy corpses remain on the floor instead of dissappearing. +const string AI_RULE_CORPSES_STAY = "AI_RULE_CORPSES_STAY"; +// Monsters will wander around when not in combat. +const string AI_RULE_WANDER = "AI_RULE_WANDER"; +// Increase the number of encounter creatures. +const string AI_INCREASE_ENC_MONSTERS = "AI_INCREASE_ENC_MONSTERS"; +// Increase all monsters hitpoints by this percentage. +const string AI_INCREASE_MONSTERS_HP = "AI_INCREASE_MONSTERS_HP"; +// Variable that can change the distance monsters can hear and see. +const string AI_RULE_MON_PERC_DISTANCE = "AI_RULE_MON_PERC_DISTANCE"; +// Variable name set to hold the maximum number of henchman the player wants. +const string AI_RULE_MAX_HENCHMAN = "AI_RULE_MAX_HENCHMAN"; +// Variable name set to hold the distance monsters can wander away. +const string AI_RULE_WANDER_DISTANCE = "AI_RULE_WANDER_DISTANCE"; +// Variable name set to allow wandering monsters to open doors. +const string AI_RULE_OPEN_DOORS = "AI_RULE_OPEN_DOORS"; +// Variable name set to hold the modules default xp scale for use later. +const string AI_RULE_DEFAULT_XP_SCALE = "AI_RULE_DEFAULT_XP_SCALE"; +// Variable name set to allow the game to regulate experience based on party size. +const string AI_RULE_PARTY_SCALE = "AI_RULE_PARTY_SCALE"; +// Variable name set to restrict the AI's use of Darkness. +const string AI_RULE_RESTRICTED_SPELLS = "AI_RULE_RESTRICTED_SPELLS"; +/*/ Special behavior constants from x0_i0_behavior +const int NW_FLAG_BEHAVIOR_SPECIAL = 0x00000001; +//Will always attack regardless of faction +const int NW_FLAG_BEHAVIOR_CARNIVORE = 0x00000002; +//Will only attack if approached +const int NW_FLAG_BEHAVIOR_OMNIVORE = 0x00000004; +//Will never attack. Will alway flee. +const int NW_FLAG_BEHAVIOR_HERBIVORE = 0x00000008; +// This is the name of the local variable that holds the spawn-in conditions +const string sSpawnCondVarname = "NW_GENERIC_MASTER"; +// The available spawn-in conditions from x0_i0_spawncond +const int NW_FLAG_ESCAPE_RETURN = 0x00000020; //Failed +const int NW_FLAG_ESCAPE_LEAVE = 0x00000040; +const int NW_FLAG_TELEPORT_RETURN = 0x00000080; //Failed +const int NW_FLAG_TELEPORT_LEAVE = 0x00000100; +const int NW_FLAG_END_COMBAT_ROUND_EVENT = 0x00004000; +const int NW_FLAG_ON_DIALOGUE_EVENT = 0x00008000; +const int NW_FLAG_AMBIENT_ANIMATIONS = 0x00080000; +const int NW_FLAG_HEARTBEAT_EVENT = 0x00100000; +const int NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS = 0x00200000; +const int NW_FLAG_DAY_NIGHT_POSTING = 0x00400000; +const int NW_FLAG_AMBIENT_ANIMATIONS_AVIAN = 0x00800000; +const string sWalkwayVarname = "NW_WALK_CONDITION"; +// If set, the creature's waypoints have been initialized. +const int NW_WALK_FLAG_INITIALIZED = 0x00000001; +// If set, the creature will walk its waypoints constantly, +// moving on in each OnHeartbeat event. Otherwise, +// it will walk to the next only when triggered by an +// OnPerception event. +const int NW_WALK_FLAG_CONSTANT = 0x00000002; +// Set when the creature is walking day waypoints. +const int NW_WALK_FLAG_IS_DAY = 0x00000004; +// Set when the creature is walking back +const int NW_WALK_FLAG_BACKWARDS = 0x00000008; diff --git a/_module/nss/0i_gui_events.nss b/_module/nss/0i_gui_events.nss new file mode 100644 index 00000000..4628cf3d --- /dev/null +++ b/_module/nss/0i_gui_events.nss @@ -0,0 +1,1032 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_gui_events +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Include scripts for all gui events. See also 0e_gui_events + + GUI Events: + GUIEVENT_EFFECTICON_CLICK: For displaying icon information. + + This was built by DAZ all credit to him. + I just changed it from PostString to a NUI menu. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_constants" +#include "0i_nui" +void ai_SetupModuleGUIEvents(object oCreature) +{ + object oModule = GetModule(); + string sModuleGUIEvents = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_GUIEVENT); + if(sModuleGUIEvents != "" || sModuleGUIEvents != "0e_gui_events") + { + SetLocalString(oModule, AI_MODULE_GUI_EVENT, sModuleGUIEvents); + } + SetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_GUIEVENT, "0e_gui_events"); +} +int EffectIconToEffectType(int nEffectIcon) +{ + switch (nEffectIcon) + { + case EFFECT_ICON_INVALID: return EFFECT_TYPE_INVALIDEFFECT; + + // *** No Extra Stats + case EFFECT_ICON_BLIND: return EFFECT_TYPE_BLINDNESS; + case EFFECT_ICON_CHARMED: return EFFECT_TYPE_CHARMED; + case EFFECT_ICON_CONFUSED: return EFFECT_TYPE_CONFUSED; + case EFFECT_ICON_FRIGHTENED: return EFFECT_TYPE_FRIGHTENED; + case EFFECT_ICON_DOMINATED: return EFFECT_TYPE_DOMINATED; + case EFFECT_ICON_PARALYZE: return EFFECT_TYPE_PARALYZE; + case EFFECT_ICON_DAZED: return EFFECT_TYPE_DAZED; + case EFFECT_ICON_STUNNED: return EFFECT_TYPE_STUNNED; + case EFFECT_ICON_SLEEP: return EFFECT_TYPE_SLEEP; + case EFFECT_ICON_SILENCE: return EFFECT_TYPE_SILENCE; + case EFFECT_ICON_TURNED: return EFFECT_TYPE_TURNED; + case EFFECT_ICON_HASTE: return EFFECT_TYPE_HASTE; + case EFFECT_ICON_SLOW: return EFFECT_TYPE_SLOW; + case EFFECT_ICON_ENTANGLE: return EFFECT_TYPE_ENTANGLE; + case EFFECT_ICON_DEAF: return EFFECT_TYPE_DEAF; + case EFFECT_ICON_DARKNESS: return EFFECT_TYPE_DARKNESS; + case EFFECT_ICON_POLYMORPH: return EFFECT_TYPE_POLYMORPH; + case EFFECT_ICON_SANCTUARY: return EFFECT_TYPE_SANCTUARY; + case EFFECT_ICON_TRUESEEING: return EFFECT_TYPE_TRUESEEING; + case EFFECT_ICON_SEEINVISIBILITY: return EFFECT_TYPE_SEEINVISIBLE; + case EFFECT_ICON_ETHEREALNESS: return EFFECT_TYPE_ETHEREAL; + case EFFECT_ICON_PETRIFIED: return EFFECT_TYPE_PETRIFY; + // *** + + case EFFECT_ICON_DAMAGE_RESISTANCE: return EFFECT_TYPE_DAMAGE_RESISTANCE; + case EFFECT_ICON_REGENERATE: return EFFECT_TYPE_REGENERATE; + case EFFECT_ICON_DAMAGE_REDUCTION: return EFFECT_TYPE_DAMAGE_REDUCTION; + case EFFECT_ICON_TEMPORARY_HITPOINTS: return EFFECT_TYPE_TEMPORARY_HITPOINTS; + case EFFECT_ICON_IMMUNITY: return EFFECT_TYPE_IMMUNITY; + case EFFECT_ICON_POISON: return EFFECT_TYPE_POISON; + case EFFECT_ICON_DISEASE: return EFFECT_TYPE_DISEASE; + case EFFECT_ICON_CURSE: return EFFECT_TYPE_CURSE; + case EFFECT_ICON_ATTACK_INCREASE: return EFFECT_TYPE_ATTACK_INCREASE; + case EFFECT_ICON_ATTACK_DECREASE: return EFFECT_TYPE_ATTACK_DECREASE; + case EFFECT_ICON_DAMAGE_INCREASE: return EFFECT_TYPE_DAMAGE_INCREASE; + case EFFECT_ICON_DAMAGE_DECREASE: return EFFECT_TYPE_DAMAGE_DECREASE; + case EFFECT_ICON_AC_INCREASE: return EFFECT_TYPE_AC_INCREASE; + case EFFECT_ICON_AC_DECREASE: return EFFECT_TYPE_AC_DECREASE; + case EFFECT_ICON_MOVEMENT_SPEED_INCREASE: return EFFECT_TYPE_MOVEMENT_SPEED_INCREASE; + case EFFECT_ICON_MOVEMENT_SPEED_DECREASE: return EFFECT_TYPE_MOVEMENT_SPEED_DECREASE; + case EFFECT_ICON_SAVING_THROW_DECREASE: return EFFECT_TYPE_SAVING_THROW_DECREASE; + case EFFECT_ICON_SPELL_RESISTANCE_INCREASE: return EFFECT_TYPE_SPELL_RESISTANCE_INCREASE; + case EFFECT_ICON_SPELL_RESISTANCE_DECREASE: return EFFECT_TYPE_SPELL_RESISTANCE_DECREASE; + case EFFECT_ICON_SKILL_INCREASE: return EFFECT_TYPE_SKILL_INCREASE; + case EFFECT_ICON_SKILL_DECREASE: return EFFECT_TYPE_SKILL_DECREASE; + case EFFECT_ICON_ELEMENTALSHIELD: return EFFECT_TYPE_ELEMENTALSHIELD; + case EFFECT_ICON_LEVELDRAIN: return EFFECT_TYPE_NEGATIVELEVEL; + case EFFECT_ICON_SPELLLEVELABSORPTION: return EFFECT_TYPE_SPELLLEVELABSORPTION; + case EFFECT_ICON_SPELLIMMUNITY: return EFFECT_TYPE_SPELL_IMMUNITY; + case EFFECT_ICON_CONCEALMENT: return EFFECT_TYPE_CONCEALMENT; + case EFFECT_ICON_EFFECT_SPELL_FAILURE: return EFFECT_TYPE_SPELL_FAILURE; + + case EFFECT_ICON_INVISIBILITY: + case EFFECT_ICON_IMPROVEDINVISIBILITY: return EFFECT_TYPE_INVISIBILITY; + + case EFFECT_ICON_ABILITY_INCREASE_STR: + case EFFECT_ICON_ABILITY_INCREASE_DEX: + case EFFECT_ICON_ABILITY_INCREASE_CON: + case EFFECT_ICON_ABILITY_INCREASE_INT: + case EFFECT_ICON_ABILITY_INCREASE_WIS: + case EFFECT_ICON_ABILITY_INCREASE_CHA: return EFFECT_TYPE_ABILITY_INCREASE; + + case EFFECT_ICON_ABILITY_DECREASE_STR: + case EFFECT_ICON_ABILITY_DECREASE_CHA: + case EFFECT_ICON_ABILITY_DECREASE_DEX: + case EFFECT_ICON_ABILITY_DECREASE_CON: + case EFFECT_ICON_ABILITY_DECREASE_INT: + case EFFECT_ICON_ABILITY_DECREASE_WIS: return EFFECT_TYPE_ABILITY_DECREASE; + + case EFFECT_ICON_IMMUNITY_ALL: + case EFFECT_ICON_IMMUNITY_MIND: + case EFFECT_ICON_IMMUNITY_POISON: + case EFFECT_ICON_IMMUNITY_DISEASE: + case EFFECT_ICON_IMMUNITY_FEAR: + case EFFECT_ICON_IMMUNITY_TRAP: + case EFFECT_ICON_IMMUNITY_PARALYSIS: + case EFFECT_ICON_IMMUNITY_BLINDNESS: + case EFFECT_ICON_IMMUNITY_DEAFNESS: + case EFFECT_ICON_IMMUNITY_SLOW: + case EFFECT_ICON_IMMUNITY_ENTANGLE: + case EFFECT_ICON_IMMUNITY_SILENCE: + case EFFECT_ICON_IMMUNITY_STUN: + case EFFECT_ICON_IMMUNITY_SLEEP: + case EFFECT_ICON_IMMUNITY_CHARM: + case EFFECT_ICON_IMMUNITY_DOMINATE: + case EFFECT_ICON_IMMUNITY_CONFUSE: + case EFFECT_ICON_IMMUNITY_CURSE: + case EFFECT_ICON_IMMUNITY_DAZED: + case EFFECT_ICON_IMMUNITY_ABILITY_DECREASE: + case EFFECT_ICON_IMMUNITY_ATTACK_DECREASE: + case EFFECT_ICON_IMMUNITY_DAMAGE_DECREASE: + case EFFECT_ICON_IMMUNITY_DAMAGE_IMMUNITY_DECREASE: + case EFFECT_ICON_IMMUNITY_AC_DECREASE: + case EFFECT_ICON_IMMUNITY_MOVEMENT_SPEED_DECREASE: + case EFFECT_ICON_IMMUNITY_SAVING_THROW_DECREASE: + case EFFECT_ICON_IMMUNITY_SPELL_RESISTANCE_DECREASE: + case EFFECT_ICON_IMMUNITY_SKILL_DECREASE: + case EFFECT_ICON_IMMUNITY_KNOCKDOWN: + case EFFECT_ICON_IMMUNITY_NEGATIVE_LEVEL: + case EFFECT_ICON_IMMUNITY_SNEAK_ATTACK: + case EFFECT_ICON_IMMUNITY_CRITICAL_HIT: + case EFFECT_ICON_IMMUNITY_DEATH_MAGIC: return EFFECT_TYPE_IMMUNITY; + + case EFFECT_ICON_SAVING_THROW_INCREASE: + case EFFECT_ICON_REFLEX_SAVE_INCREASED: + case EFFECT_ICON_FORT_SAVE_INCREASED: + case EFFECT_ICON_WILL_SAVE_INCREASED: return EFFECT_TYPE_SAVING_THROW_INCREASE; + + case EFFECT_ICON_DAMAGE_IMMUNITY_INCREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_MAGIC: + case EFFECT_ICON_DAMAGE_IMMUNITY_ACID: + case EFFECT_ICON_DAMAGE_IMMUNITY_COLD: + case EFFECT_ICON_DAMAGE_IMMUNITY_DIVINE: + case EFFECT_ICON_DAMAGE_IMMUNITY_ELECTRICAL: + case EFFECT_ICON_DAMAGE_IMMUNITY_FIRE: + case EFFECT_ICON_DAMAGE_IMMUNITY_NEGATIVE: + case EFFECT_ICON_DAMAGE_IMMUNITY_POSITIVE: + case EFFECT_ICON_DAMAGE_IMMUNITY_SONIC: return EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE; + + case EFFECT_ICON_DAMAGE_IMMUNITY_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_MAGIC_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_ACID_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_COLD_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_DIVINE_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_ELECTRICAL_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_FIRE_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_NEGATIVE_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_POSITIVE_DECREASE: + case EFFECT_ICON_DAMAGE_IMMUNITY_SONIC_DECREASE: return EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE; + + //case EFFECT_ICON_INVULNERABLE: return EFFECT_TYPE_INVULNERABLE; + //case EFFECT_ICON_WOUNDING: return EFFECT_TYPE_INVALIDEFFECT; + //case EFFECT_ICON_TAUNTED: return EFFECT_TYPE_INVALIDEFFECT; + //case EFFECT_ICON_TIMESTOP: return EFFECT_TYPE_TIMESTOP; + //case EFFECT_ICON_BLINDNESS: return EFFECT_TYPE_BLINDNESS; + //case EFFECT_ICON_DISPELMAGICBEST: return EFFECT_TYPE_INVALIDEFFECT; + //case EFFECT_ICON_DISPELMAGICALL: return EFFECT_TYPE_INVALIDEFFECT; + //case EFFECT_ICON_ENEMY_ATTACK_BONUS: return EFFECT_TYPE_INVALIDEFFECT; + //case EFFECT_ICON_FATIGUE: return EFFECT_TYPE_INVALIDEFFECT; + } + return EFFECT_TYPE_INVALIDEFFECT; +} +int AbilityTypeFromEffectIconAbility(int nEffectIcon) +{ + switch (nEffectIcon) + { + case EFFECT_ICON_ABILITY_INCREASE_STR: + case EFFECT_ICON_ABILITY_DECREASE_STR: + return ABILITY_STRENGTH; + case EFFECT_ICON_ABILITY_INCREASE_DEX: + case EFFECT_ICON_ABILITY_DECREASE_DEX: + return ABILITY_DEXTERITY; + case EFFECT_ICON_ABILITY_INCREASE_CON: + case EFFECT_ICON_ABILITY_DECREASE_CON: + return ABILITY_CONSTITUTION; + case EFFECT_ICON_ABILITY_INCREASE_INT: + case EFFECT_ICON_ABILITY_DECREASE_INT: + return ABILITY_INTELLIGENCE; + case EFFECT_ICON_ABILITY_INCREASE_WIS: + case EFFECT_ICON_ABILITY_DECREASE_WIS: + return ABILITY_WISDOM; + case EFFECT_ICON_ABILITY_INCREASE_CHA: + case EFFECT_ICON_ABILITY_DECREASE_CHA: + return ABILITY_CHARISMA; + } + return -1; +} +int DamageTypeFromEffectIconDamageImmunity(int nEffectIcon) +{ + switch (nEffectIcon) + { + case EFFECT_ICON_DAMAGE_IMMUNITY_MAGIC: + case EFFECT_ICON_DAMAGE_IMMUNITY_MAGIC_DECREASE: + return DAMAGE_TYPE_MAGICAL; + case EFFECT_ICON_DAMAGE_IMMUNITY_ACID: + case EFFECT_ICON_DAMAGE_IMMUNITY_ACID_DECREASE: + return DAMAGE_TYPE_ACID; + case EFFECT_ICON_DAMAGE_IMMUNITY_COLD: + case EFFECT_ICON_DAMAGE_IMMUNITY_COLD_DECREASE: + return DAMAGE_TYPE_COLD; + case EFFECT_ICON_DAMAGE_IMMUNITY_DIVINE: + case EFFECT_ICON_DAMAGE_IMMUNITY_DIVINE_DECREASE: + return DAMAGE_TYPE_DIVINE; + case EFFECT_ICON_DAMAGE_IMMUNITY_ELECTRICAL: + case EFFECT_ICON_DAMAGE_IMMUNITY_ELECTRICAL_DECREASE: + return DAMAGE_TYPE_ELECTRICAL; + case EFFECT_ICON_DAMAGE_IMMUNITY_FIRE: + case EFFECT_ICON_DAMAGE_IMMUNITY_FIRE_DECREASE: + return DAMAGE_TYPE_FIRE; + case EFFECT_ICON_DAMAGE_IMMUNITY_NEGATIVE: + case EFFECT_ICON_DAMAGE_IMMUNITY_NEGATIVE_DECREASE: + return DAMAGE_TYPE_NEGATIVE; + case EFFECT_ICON_DAMAGE_IMMUNITY_POSITIVE: + case EFFECT_ICON_DAMAGE_IMMUNITY_POSITIVE_DECREASE: + return DAMAGE_TYPE_POSITIVE; + case EFFECT_ICON_DAMAGE_IMMUNITY_SONIC: + case EFFECT_ICON_DAMAGE_IMMUNITY_SONIC_DECREASE: + return DAMAGE_TYPE_SONIC; + } + return -1; +} + +int ImmunityTypeFromEffectIconImmunity(int nEffectIcon) +{ + switch (nEffectIcon) + { + case EFFECT_ICON_IMMUNITY_MIND: return IMMUNITY_TYPE_MIND_SPELLS; + case EFFECT_ICON_IMMUNITY_POISON: return IMMUNITY_TYPE_POISON; + case EFFECT_ICON_IMMUNITY_DISEASE: return IMMUNITY_TYPE_DISEASE; + case EFFECT_ICON_IMMUNITY_FEAR: return IMMUNITY_TYPE_FEAR; + case EFFECT_ICON_IMMUNITY_TRAP: return IMMUNITY_TYPE_TRAP; + case EFFECT_ICON_IMMUNITY_PARALYSIS: return IMMUNITY_TYPE_PARALYSIS; + case EFFECT_ICON_IMMUNITY_BLINDNESS: return IMMUNITY_TYPE_BLINDNESS; + case EFFECT_ICON_IMMUNITY_DEAFNESS: return IMMUNITY_TYPE_DEAFNESS; + case EFFECT_ICON_IMMUNITY_SLOW: return IMMUNITY_TYPE_SLOW; + case EFFECT_ICON_IMMUNITY_ENTANGLE: return IMMUNITY_TYPE_ENTANGLE; + case EFFECT_ICON_IMMUNITY_SILENCE: return IMMUNITY_TYPE_SILENCE; + case EFFECT_ICON_IMMUNITY_STUN: return IMMUNITY_TYPE_STUN; + case EFFECT_ICON_IMMUNITY_SLEEP: return IMMUNITY_TYPE_SLEEP; + case EFFECT_ICON_IMMUNITY_CHARM: return IMMUNITY_TYPE_CHARM; + case EFFECT_ICON_IMMUNITY_DOMINATE: return IMMUNITY_TYPE_DOMINATE; + case EFFECT_ICON_IMMUNITY_CONFUSE: return IMMUNITY_TYPE_CONFUSED; + case EFFECT_ICON_IMMUNITY_CURSE: return IMMUNITY_TYPE_CURSED; + case EFFECT_ICON_IMMUNITY_DAZED: return IMMUNITY_TYPE_DAZED; + case EFFECT_ICON_IMMUNITY_ABILITY_DECREASE: return IMMUNITY_TYPE_ABILITY_DECREASE; + case EFFECT_ICON_IMMUNITY_ATTACK_DECREASE: return IMMUNITY_TYPE_ATTACK_DECREASE; + case EFFECT_ICON_IMMUNITY_DAMAGE_DECREASE: return IMMUNITY_TYPE_DAMAGE_DECREASE; + case EFFECT_ICON_IMMUNITY_DAMAGE_IMMUNITY_DECREASE: return IMMUNITY_TYPE_DAMAGE_IMMUNITY_DECREASE; + case EFFECT_ICON_IMMUNITY_AC_DECREASE: return IMMUNITY_TYPE_AC_DECREASE; + case EFFECT_ICON_IMMUNITY_MOVEMENT_SPEED_DECREASE: return IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE; + case EFFECT_ICON_IMMUNITY_SAVING_THROW_DECREASE: return IMMUNITY_TYPE_SAVING_THROW_DECREASE; + case EFFECT_ICON_IMMUNITY_SPELL_RESISTANCE_DECREASE: return IMMUNITY_TYPE_SPELL_RESISTANCE_DECREASE; + case EFFECT_ICON_IMMUNITY_SKILL_DECREASE: return IMMUNITY_TYPE_SKILL_DECREASE; + case EFFECT_ICON_IMMUNITY_KNOCKDOWN: return IMMUNITY_TYPE_KNOCKDOWN; + case EFFECT_ICON_IMMUNITY_NEGATIVE_LEVEL: return IMMUNITY_TYPE_NEGATIVE_LEVEL; + case EFFECT_ICON_IMMUNITY_SNEAK_ATTACK: return IMMUNITY_TYPE_SNEAK_ATTACK; + case EFFECT_ICON_IMMUNITY_CRITICAL_HIT: return IMMUNITY_TYPE_CRITICAL_HIT; + case EFFECT_ICON_IMMUNITY_DEATH_MAGIC: return IMMUNITY_TYPE_DEATH; + } + return -1; +} +void ClearLines(object oPlayer) +{ + int nLine, nLines = GetLocalInt(oPlayer, "BUFFINFO_LAST_NUM_LINES"); + for (nLine = 1; nLine <= nLines; nLine++) + { + PostString(oPlayer, "", 10, nLine + 3, SCREEN_ANCHOR_TOP_RIGHT, 0.1f, 0xFFFFFF00, 0xFFFFFF00, nLine); + } +} +void DisplayLine(object oPlayer, int nLine, string sText, int nColor) +{ + PostString(oPlayer, sText, 10, nLine + 3, SCREEN_ANCHOR_TOP_RIGHT, 10.0f, nColor, 0xFFFFFF00, nLine); +} +string SecondsToTimestamp(int nSeconds) +{ + sqlquery sql; + if (nSeconds > 86400) sql = SqlPrepareQueryObject(GetModule(), "SELECT (@seconds / 3600) || ':' || strftime('%M:%S', @seconds / 86400.0);"); + else sql = SqlPrepareQueryObject(GetModule(), "SELECT time(@seconds, 'unixepoch');"); + SqlBindInt(sql, "@seconds", nSeconds); + SqlStep(sql); + return SqlGetString(sql, 0); +} +string Get2DAStrRef(string s2DA, string sColumn, int nRow) +{ + return GetStringByStrRef(StringToInt(Get2DAString(s2DA, sColumn, nRow))); +} +string GetVersusRacialTypeAndAlignment(int nRacialType, int nLawfulChaotic, int nGoodEvil) +{ + string sRacialType = nRacialType == RACIAL_TYPE_INVALID ? "" : Get2DAStrRef("racialtypes", "NamePlural", nRacialType); + string sLawfulChaotic = nLawfulChaotic == ALIGNMENT_LAWFUL ? "Lawful" : nLawfulChaotic == ALIGNMENT_CHAOTIC ? "Chaotic" : ""; + string sGoodEvil = nGoodEvil == ALIGNMENT_GOOD ? "Good" : nGoodEvil == ALIGNMENT_EVIL ? "Evil" : ""; + string sAlignment = sLawfulChaotic + (sLawfulChaotic == "" ? sGoodEvil : (sGoodEvil == "" ? "" : " " + sGoodEvil)); + return (sRacialType != "" || sAlignment != "") ? (" vs. " + sAlignment + (sAlignment == "" ? sRacialType : (sRacialType == "" ? "" : " " + sRacialType))) : ""; +} +string GetModifierType(int nEffectType, int nPlus, int nMinus) +{ + return nEffectType == nPlus ? "+" : nEffectType == nMinus ? "-" : ""; +} +string ACTypeToString(int nACType) +{ + switch (nACType) + { + case AC_DODGE_BONUS: return "Dodge"; + case AC_NATURAL_BONUS: return "Natural"; + case AC_ARMOUR_ENCHANTMENT_BONUS: return "Armor"; + case AC_SHIELD_ENCHANTMENT_BONUS: return "Shield"; + case AC_DEFLECTION_BONUS: return "Deflection"; + } + return ""; +} + +string SavingThrowToString(int nSavingThrow) +{ + switch (nSavingThrow) + { + case SAVING_THROW_ALL: return "All"; + case SAVING_THROW_FORT: return "Fortitude"; + case SAVING_THROW_REFLEX: return "Reflex"; + case SAVING_THROW_WILL: return "Will"; + } + return ""; +} +string SavingThrowTypeToString(int nSavingThrowType) +{ + switch (nSavingThrowType) + { + case SAVING_THROW_TYPE_MIND_SPELLS: return "Mind Spells"; + case SAVING_THROW_TYPE_POISON: return "Poison"; + case SAVING_THROW_TYPE_DISEASE: return "Disease"; + case SAVING_THROW_TYPE_FEAR: return "Fear"; + case SAVING_THROW_TYPE_SONIC: return "Sonic"; + case SAVING_THROW_TYPE_ACID: return "Acid"; + case SAVING_THROW_TYPE_FIRE: return "Fire"; + case SAVING_THROW_TYPE_ELECTRICITY: return "Electricity"; + case SAVING_THROW_TYPE_POSITIVE: return "Positive"; + case SAVING_THROW_TYPE_NEGATIVE: return "Negative"; + case SAVING_THROW_TYPE_DEATH: return "Death"; + case SAVING_THROW_TYPE_COLD: return "Cold"; + case SAVING_THROW_TYPE_DIVINE: return "Divine"; + case SAVING_THROW_TYPE_TRAP: return "Traps"; + case SAVING_THROW_TYPE_SPELL: return "Spells"; + case SAVING_THROW_TYPE_GOOD: return "Good"; + case SAVING_THROW_TYPE_EVIL: return "Evil"; + case SAVING_THROW_TYPE_LAW: return "Lawful"; + case SAVING_THROW_TYPE_CHAOS: return "Chaotic"; + } + return ""; +} +string AbilityToString(int nAbility) +{ + switch (nAbility) + { + case ABILITY_STRENGTH: return "Strength"; + case ABILITY_DEXTERITY: return "Dexterity"; + case ABILITY_CONSTITUTION: return "Constitution"; + case ABILITY_INTELLIGENCE: return "Intelligence"; + case ABILITY_WISDOM: return "Wisdom"; + case ABILITY_CHARISMA: return "Charisma"; + } + return ""; +} +string DamageTypeToString(int nDamageType) +{ + switch (nDamageType) + { + case DAMAGE_TYPE_BLUDGEONING: return "Bludgeoning"; + case DAMAGE_TYPE_PIERCING: return "Piercing"; + case DAMAGE_TYPE_SLASHING: return "Slashing"; + case DAMAGE_TYPE_MAGICAL: return "Magical"; + case DAMAGE_TYPE_ACID: return "Acid"; + case DAMAGE_TYPE_COLD: return "Cold"; + case DAMAGE_TYPE_DIVINE: return "Divine"; + case DAMAGE_TYPE_ELECTRICAL: return "Electrical"; + case DAMAGE_TYPE_FIRE: return "Fire"; + case DAMAGE_TYPE_NEGATIVE: return "Negative"; + case DAMAGE_TYPE_POSITIVE: return "Positive"; + case DAMAGE_TYPE_SONIC: return "Sonic"; + case DAMAGE_TYPE_BASE_WEAPON: return "Base Weapon"; + } + return ""; +} +string SpellSchoolToString(int nSpellSchool) +{ + switch (nSpellSchool) + { + case SPELL_SCHOOL_GENERAL: return "General"; + case SPELL_SCHOOL_ABJURATION: return "Abjuration"; + case SPELL_SCHOOL_CONJURATION: return "Conjuration"; + case SPELL_SCHOOL_DIVINATION: return "Divination"; + case SPELL_SCHOOL_ENCHANTMENT: return "Enchantment"; + case SPELL_SCHOOL_EVOCATION: return "Evocation"; + case SPELL_SCHOOL_ILLUSION: return "Illusion"; + case SPELL_SCHOOL_NECROMANCY: return "Necromancy"; + case SPELL_SCHOOL_TRANSMUTATION: return "Transmutation"; + } + return ""; +} +string MissChanceToString(int nMissChance) +{ + switch (nMissChance) + { + case MISS_CHANCE_TYPE_VS_RANGED: return "vs. Ranged"; + case MISS_CHANCE_TYPE_VS_MELEE: return "vs. Melee"; + } + return ""; +} +void ai_CreateEffectChatReport(object oPlayer, int nEffectIconID) +{ + int nIconEffectType = EffectIconToEffectType(nEffectIconID); + if(nIconEffectType == EFFECT_TYPE_INVALIDEFFECT) return; + int nLine, nIndex, nEffectIndex; + string sColor = AI_COLOR_YELLOW; + int bSkipDisplay, bHasEffect; + int nEffectType, bIsSpellLevelAbsorptionPretendingToBeSpellImmunity; + string sText; + json jEffectID = JsonArray(); + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 500 / 27 + sText = Get2DAStrRef("effecticons", "StrRef", nEffectIconID); + ai_SendMessages(sText, AI_COLOR_YELLOW, oPlayer); + effect eEffect = GetFirstEffect(oPlayer); + while(GetIsEffectValid(eEffect)) + { + bSkipDisplay = FALSE; + nEffectType = GetEffectType(eEffect); + // Unlimited EffectSpellLevelAbsorption has a SpellImmunity Icon + if (nIconEffectType == EFFECT_TYPE_SPELL_IMMUNITY && GetEffectInteger(eEffect, 3)) + { + bIsSpellLevelAbsorptionPretendingToBeSpellImmunity = TRUE; + nIconEffectType = EFFECT_TYPE_SPELLLEVELABSORPTION; + } + if (nEffectType == nIconEffectType) + { + bHasEffect = TRUE; + int nSpellID = GetEffectSpellId(eEffect); + string sSpellName = nSpellID == -1 ? "" : Get2DAStrRef("spells", "Name", nSpellID); + int bIsPermanentEffect = GetEffectDurationType(eEffect) == DURATION_TYPE_PERMANENT; + int nDurationRemaining = GetEffectDurationRemaining(eEffect); + string sDurationRemaining = bIsPermanentEffect ? "(Permanent)" : "(" + SecondsToTimestamp(nDurationRemaining) + ")"; + if(bIsPermanentEffect) sColor = AI_COLOR_WHITE; + else + { + if(nDurationRemaining < 61) sColor = AI_COLOR_RED; + else if(nDurationRemaining < 300) sColor = AI_COLOR_YELLOW; + else sColor = AI_COLOR_GREEN; + } + string sStats = ""; + string sRacialTypeAlignment = ""; + switch (nEffectType) + { + case EFFECT_TYPE_AC_INCREASE: + case EFFECT_TYPE_AC_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_AC_INCREASE, EFFECT_TYPE_AC_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + " " + ACTypeToString(GetEffectInteger(eEffect, 0)) + " AC"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_ATTACK_INCREASE: + case EFFECT_TYPE_ATTACK_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_ATTACK_INCREASE, EFFECT_TYPE_ATTACK_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) +" AB"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_SAVING_THROW_INCREASE: + case EFFECT_TYPE_SAVING_THROW_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_SAVING_THROW_INCREASE, EFFECT_TYPE_SAVING_THROW_DECREASE); + string sSavingThrow = SavingThrowToString(GetEffectInteger(eEffect, 1)); + string sSavingThrowType = SavingThrowTypeToString(GetEffectInteger(eEffect, 2)); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) + " " + sSavingThrow + (sSavingThrowType == "" ? "" : " (vs. " + sSavingThrowType + ")"); + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4), GetEffectInteger(eEffect, 5)); + break; + } + case EFFECT_TYPE_ABILITY_INCREASE: + case EFFECT_TYPE_ABILITY_DECREASE: + { + int nAbility = AbilityTypeFromEffectIconAbility(nEffectIconID); + + if (nAbility != GetEffectInteger(eEffect, 0)) + bSkipDisplay = TRUE; + else + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_ABILITY_INCREASE, EFFECT_TYPE_ABILITY_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + " " + AbilityToString(nAbility); + } + break; + } + case EFFECT_TYPE_DAMAGE_INCREASE: + case EFFECT_TYPE_DAMAGE_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_DAMAGE_INCREASE, EFFECT_TYPE_DAMAGE_DECREASE); + sStats = sModifier + Get2DAStrRef("iprp_damagecost", "Name", GetEffectInteger(eEffect, 0)) + " (" + DamageTypeToString(GetEffectInteger(eEffect, 1)) + ")"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_SKILL_INCREASE: + case EFFECT_TYPE_SKILL_DECREASE: + { + int nSkill = GetEffectInteger(eEffect, 0); + string sSkill = nSkill == 255 ? "All Skills" : Get2DAStrRef("skills", "Name", nSkill); + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_SKILL_INCREASE, EFFECT_TYPE_SKILL_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + " " + sSkill; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_TEMPORARY_HITPOINTS: + { + sStats = "+" + IntToString(GetEffectInteger(eEffect, 0)) + " HitPoints"; + break; + } + case EFFECT_TYPE_DAMAGE_REDUCTION: + { + int nAmount = GetEffectInteger(eEffect, 0); + int nDamagePower = GetEffectInteger(eEffect, 1); + nDamagePower = nDamagePower > 6 ? --nDamagePower : nDamagePower; + int nRemaining = GetEffectInteger(eEffect, 2); + sStats = IntToString(nAmount) + "/+" + IntToString(nDamagePower) + " (" + (nRemaining == 0 ? "Unlimited" : IntToString(nRemaining) + " Damage Remaining") + ")"; + break; + } + case EFFECT_TYPE_DAMAGE_RESISTANCE: + { + int nAmount = GetEffectInteger(eEffect, 1); + int nRemaining = GetEffectInteger(eEffect, 2); + sStats = IntToString(nAmount) + "/- " + DamageTypeToString(GetEffectInteger(eEffect, 0)) + " Resistance (" + (nRemaining == 0 ? "Unlimited" : IntToString(nRemaining) + " Damage Remaining") + ")"; + break; + } + case EFFECT_TYPE_IMMUNITY: + { + int nImmunity = ImmunityTypeFromEffectIconImmunity(nEffectIconID); + + if (nImmunity != GetEffectInteger(eEffect, 0)) + bSkipDisplay = TRUE; + else + { + sStats = Get2DAStrRef("effecticons", "StrRef", nEffectIconID); + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 1), GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3)); + } + break; + } + case EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE: + case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE: + { + int nDamageType = GetEffectInteger(eEffect, 0); + int nDamageTypeFromIcon = DamageTypeFromEffectIconDamageImmunity(nEffectIconID); + + if (nDamageTypeFromIcon != -1 && nDamageType != nDamageTypeFromIcon) + bSkipDisplay = TRUE; + + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE, EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + "% " + DamageTypeToString(nDamageType) + " Damage Immunity"; + break; + } + case EFFECT_TYPE_SPELL_IMMUNITY: + { + sStats = "Spell Immunity: " + Get2DAStrRef("spells", "Name", GetEffectInteger(eEffect, 0)); + break; + } + case EFFECT_TYPE_SPELLLEVELABSORPTION: + { + int nMaxSpellLevelAbsorbed = GetEffectInteger(eEffect, 0); + int bUnlimited = GetEffectInteger(eEffect, 3); + string sSpellLevel; + switch (nMaxSpellLevelAbsorbed) + { + case 0: sSpellLevel = "Cantrip"; break; + case 1: sSpellLevel = "1st"; break; + case 2: sSpellLevel = "2nd"; break; + case 3: sSpellLevel = "3rd"; break; + default: sSpellLevel = IntToString(nMaxSpellLevelAbsorbed) + "th"; break; + } + sSpellLevel += " Level" + (nMaxSpellLevelAbsorbed == 0 ? "" : " and Below"); + string sSpellSchool = SpellSchoolToString(GetEffectInteger(eEffect, 2)); + string sRemainingSpellLevels = bUnlimited ? "" : "(" + IntToString(GetEffectInteger(eEffect, 1)) + " Spell Levels Remaining)"; + sStats = sSpellLevel + " " + sSpellSchool + " Spell Immunity " + sRemainingSpellLevels; + + if (bIsSpellLevelAbsorptionPretendingToBeSpellImmunity) + nIconEffectType = EFFECT_TYPE_SPELL_IMMUNITY; + else if (bUnlimited && !bIsSpellLevelAbsorptionPretendingToBeSpellImmunity) + bSkipDisplay = TRUE; + + break; + } + case EFFECT_TYPE_REGENERATE: + { + sStats = "+" + IntToString(GetEffectInteger(eEffect, 0)) + " HP / " + FloatToString((GetEffectInteger(eEffect, 1) / 1000.0f), 0, 2) + "s"; + break; + } + case EFFECT_TYPE_POISON: + { + sStats = "Poison: " + Get2DAStrRef("poison", "Name", GetEffectInteger(eEffect, 0)); + break; + } + case EFFECT_TYPE_DISEASE: + { + sStats = "Disease: " + Get2DAStrRef("disease", "Name", GetEffectInteger(eEffect, 0)); + break; + } + case EFFECT_TYPE_CURSE: + { + int nAbility; + string sAbilityDecrease; + for (nAbility = 0; nAbility < 6; nAbility++) + { + int nAbilityMod = GetEffectInteger(eEffect, nAbility); + if (nAbilityMod > 0) + { + string sAbility = GetStringLeft(AbilityToString(nAbility), 3); + sAbilityDecrease += "-" + IntToString(nAbilityMod) + " " + sAbility + ", "; + } + } + sAbilityDecrease = GetStringLeft(sAbilityDecrease, GetStringLength(sAbilityDecrease) - 2); + sStats = sAbilityDecrease; + break; + } + case EFFECT_TYPE_MOVEMENT_SPEED_INCREASE: + case EFFECT_TYPE_MOVEMENT_SPEED_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_MOVEMENT_SPEED_INCREASE, EFFECT_TYPE_MOVEMENT_SPEED_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) + "% Movement Speed"; + break; + } + case EFFECT_TYPE_ELEMENTALSHIELD: + { + sStats = IntToString(GetEffectInteger(eEffect, 0)) + " + " + Get2DAStrRef("iprp_damagecost", "Name", GetEffectInteger(eEffect, 1)) + " (" + DamageTypeToString(GetEffectInteger(eEffect, 2)) + ")"; + break; + } + case EFFECT_TYPE_NEGATIVELEVEL: + { + sStats = "-" + IntToString(GetEffectInteger(eEffect, 0)) + " Levels"; + break; + } + case EFFECT_TYPE_CONCEALMENT: + { + string sMissChance = MissChanceToString(GetEffectInteger(eEffect, 4) - 1); + sStats = IntToString(GetEffectInteger(eEffect, 0)) + "% Concealment" + (sMissChance == "" ? "" : " (" + sMissChance + ")"); + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 1), GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3)); + break; + } + case EFFECT_TYPE_SPELL_RESISTANCE_INCREASE: + case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_SPELL_RESISTANCE_INCREASE, EFFECT_TYPE_SPELL_RESISTANCE_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) + " Spell Resistance"; + break; + } + case EFFECT_TYPE_SPELL_FAILURE: + { + sStats = IntToString(GetEffectInteger(eEffect, 0)) + "% Spell Failure (Spell School: " + SpellSchoolToString(GetEffectInteger(eEffect, 1)) + ")"; + break; + } + case EFFECT_TYPE_INVISIBILITY: + { + int nInvisibilityType = GetEffectInteger(eEffect, 0); + if (nEffectIconID == EFFECT_ICON_INVISIBILITY) + bSkipDisplay = nInvisibilityType != INVISIBILITY_TYPE_NORMAL; + else if (nEffectIconID == EFFECT_ICON_IMPROVEDINVISIBILITY) + bSkipDisplay = nInvisibilityType != INVISIBILITY_TYPE_IMPROVED; + if (!bSkipDisplay) + { + sStats = (nInvisibilityType == INVISIBILITY_TYPE_IMPROVED ? "Improved " : "") + "Invisibility"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 1), GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3)); + } + break; + } + case EFFECT_TYPE_HASTE: + { + sStats = "Hasted"; + } + } + if(!bSkipDisplay) + { + sText = sSpellName + " " + sDurationRemaining + (sStats == "" ? "" : " -> " + sStats + sRacialTypeAlignment); + if(sText != "") + { + ai_SendMessages(sText, sColor, oPlayer); + object oSource = GetEffectCreator(eEffect); + if(GetIsObjectValid(oSource)) + { + sText = GetObjectType(oSource) ? GetName(oSource) : ""; + sText = " Creator: " + sText; + float fLength = IntToFloat(GetStringLength(sText) * 8); + ai_SendMessages(sText, AI_COLOR_YELLOW, oPlayer); + } + } + } + } + nIndex++; + eEffect = GetNextEffect(oPlayer); + } +} +void ai_CreateEffectIconMenu(object oPlayer, int nEffectIconID) +{ + int nIconEffectType = EffectIconToEffectType(nEffectIconID); + if(nIconEffectType == EFFECT_TYPE_INVALIDEFFECT) return; + int nLine, nColor, nIndex, nEffectIndex; + int bSkipDisplay, bHasEffect; + int nEffectType, bIsSpellLevelAbsorptionPretendingToBeSpellImmunity; + string sText; + json jEffectID = JsonArray(); + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 500 / 27 + sText = Get2DAStrRef("effecticons", "StrRef", nEffectIconID); + json jRow = CreateLabel(JsonArray(), "Effect: " + sText, "lbl_buff_name", 700.0f, 15.0f, NUI_HALIGN_LEFT, NUI_VALIGN_MIDDLE, 0.0); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + float fHeight = 27.0; + effect eEffect = GetFirstEffect(oPlayer); + while(GetIsEffectValid(eEffect)) + { + bSkipDisplay = FALSE; + nEffectType = GetEffectType(eEffect); + // Unlimited EffectSpellLevelAbsorption has a SpellImmunity Icon + if (nIconEffectType == EFFECT_TYPE_SPELL_IMMUNITY && GetEffectInteger(eEffect, 3)) + { + bIsSpellLevelAbsorptionPretendingToBeSpellImmunity = TRUE; + nIconEffectType = EFFECT_TYPE_SPELLLEVELABSORPTION; + } + if (nEffectType == nIconEffectType) + { + bHasEffect = TRUE; + int nSpellID = GetEffectSpellId(eEffect); + string sSpellName = nSpellID == -1 ? "" : Get2DAStrRef("spells", "Name", nSpellID); + int bIsPermanentEffect = GetEffectDurationType(eEffect) == DURATION_TYPE_PERMANENT; + int nDurationRemaining = GetEffectDurationRemaining(eEffect); + string sDurationRemaining = bIsPermanentEffect ? "(Permanent)" : "(" + SecondsToTimestamp(nDurationRemaining) + ")"; + if(bIsPermanentEffect) nColor = 0x0000FFFF; + else + { + float fPercentage = IntToFloat(nDurationRemaining) / IntToFloat(GetEffectDuration(eEffect)); + if(fPercentage > 0.5f) nColor = 0x00FF00FF; + else if(fPercentage < 0.25f) nColor = 0xFF0000FF; + else nColor = 0xFFFF00FF; + } + string sStats = ""; + string sRacialTypeAlignment = ""; + switch (nEffectType) + { + case EFFECT_TYPE_AC_INCREASE: + case EFFECT_TYPE_AC_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_AC_INCREASE, EFFECT_TYPE_AC_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + " " + ACTypeToString(GetEffectInteger(eEffect, 0)) + " AC"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_ATTACK_INCREASE: + case EFFECT_TYPE_ATTACK_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_ATTACK_INCREASE, EFFECT_TYPE_ATTACK_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) +" AB"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_SAVING_THROW_INCREASE: + case EFFECT_TYPE_SAVING_THROW_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_SAVING_THROW_INCREASE, EFFECT_TYPE_SAVING_THROW_DECREASE); + string sSavingThrow = SavingThrowToString(GetEffectInteger(eEffect, 1)); + string sSavingThrowType = SavingThrowTypeToString(GetEffectInteger(eEffect, 2)); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) + " " + sSavingThrow + (sSavingThrowType == "" ? "" : " (vs. " + sSavingThrowType + ")"); + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4), GetEffectInteger(eEffect, 5)); + break; + } + case EFFECT_TYPE_ABILITY_INCREASE: + case EFFECT_TYPE_ABILITY_DECREASE: + { + int nAbility = AbilityTypeFromEffectIconAbility(nEffectIconID); + + if (nAbility != GetEffectInteger(eEffect, 0)) + bSkipDisplay = TRUE; + else + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_ABILITY_INCREASE, EFFECT_TYPE_ABILITY_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + " " + AbilityToString(nAbility); + } + break; + } + case EFFECT_TYPE_DAMAGE_INCREASE: + case EFFECT_TYPE_DAMAGE_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_DAMAGE_INCREASE, EFFECT_TYPE_DAMAGE_DECREASE); + sStats = sModifier + Get2DAStrRef("iprp_damagecost", "Name", GetEffectInteger(eEffect, 0)) + " (" + DamageTypeToString(GetEffectInteger(eEffect, 1)) + ")"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_SKILL_INCREASE: + case EFFECT_TYPE_SKILL_DECREASE: + { + int nSkill = GetEffectInteger(eEffect, 0); + string sSkill = nSkill == 255 ? "All Skills" : Get2DAStrRef("skills", "Name", nSkill); + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_SKILL_INCREASE, EFFECT_TYPE_SKILL_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + " " + sSkill; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3), GetEffectInteger(eEffect, 4)); + break; + } + case EFFECT_TYPE_TEMPORARY_HITPOINTS: + { + sStats = "+" + IntToString(GetEffectInteger(eEffect, 0)) + " HitPoints"; + break; + } + case EFFECT_TYPE_DAMAGE_REDUCTION: + { + int nAmount = GetEffectInteger(eEffect, 0); + int nDamagePower = GetEffectInteger(eEffect, 1); + nDamagePower = nDamagePower > 6 ? --nDamagePower : nDamagePower; + int nRemaining = GetEffectInteger(eEffect, 2); + sStats = IntToString(nAmount) + "/+" + IntToString(nDamagePower) + " (" + (nRemaining == 0 ? "Unlimited" : IntToString(nRemaining) + " Damage Remaining") + ")"; + break; + } + case EFFECT_TYPE_DAMAGE_RESISTANCE: + { + int nAmount = GetEffectInteger(eEffect, 1); + int nRemaining = GetEffectInteger(eEffect, 2); + sStats = IntToString(nAmount) + "/- " + DamageTypeToString(GetEffectInteger(eEffect, 0)) + " Resistance (" + (nRemaining == 0 ? "Unlimited" : IntToString(nRemaining) + " Damage Remaining") + ")"; + break; + } + case EFFECT_TYPE_IMMUNITY: + { + int nImmunity = ImmunityTypeFromEffectIconImmunity(nEffectIconID); + + if (nImmunity != GetEffectInteger(eEffect, 0)) + bSkipDisplay = TRUE; + else + { + sStats = Get2DAStrRef("effecticons", "StrRef", nEffectIconID); + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 1), GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3)); + } + break; + } + case EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE: + case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE: + { + int nDamageType = GetEffectInteger(eEffect, 0); + int nDamageTypeFromIcon = DamageTypeFromEffectIconDamageImmunity(nEffectIconID); + + if (nDamageTypeFromIcon != -1 && nDamageType != nDamageTypeFromIcon) + bSkipDisplay = TRUE; + + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE, EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 1)) + "% " + DamageTypeToString(nDamageType) + " Damage Immunity"; + break; + } + case EFFECT_TYPE_SPELL_IMMUNITY: + { + sStats = "Spell Immunity: " + Get2DAStrRef("spells", "Name", GetEffectInteger(eEffect, 0)); + break; + } + case EFFECT_TYPE_SPELLLEVELABSORPTION: + { + int nMaxSpellLevelAbsorbed = GetEffectInteger(eEffect, 0); + int bUnlimited = GetEffectInteger(eEffect, 3); + string sSpellLevel; + switch (nMaxSpellLevelAbsorbed) + { + case 0: sSpellLevel = "Cantrip"; break; + case 1: sSpellLevel = "1st"; break; + case 2: sSpellLevel = "2nd"; break; + case 3: sSpellLevel = "3rd"; break; + default: sSpellLevel = IntToString(nMaxSpellLevelAbsorbed) + "th"; break; + } + sSpellLevel += " Level" + (nMaxSpellLevelAbsorbed == 0 ? "" : " and Below"); + string sSpellSchool = SpellSchoolToString(GetEffectInteger(eEffect, 2)); + string sRemainingSpellLevels = bUnlimited ? "" : "(" + IntToString(GetEffectInteger(eEffect, 1)) + " Spell Levels Remaining)"; + sStats = sSpellLevel + " " + sSpellSchool + " Spell Immunity " + sRemainingSpellLevels; + + if (bIsSpellLevelAbsorptionPretendingToBeSpellImmunity) + nIconEffectType = EFFECT_TYPE_SPELL_IMMUNITY; + else if (bUnlimited && !bIsSpellLevelAbsorptionPretendingToBeSpellImmunity) + bSkipDisplay = TRUE; + + break; + } + case EFFECT_TYPE_REGENERATE: + { + sStats = "+" + IntToString(GetEffectInteger(eEffect, 0)) + " HP / " + FloatToString((GetEffectInteger(eEffect, 1) / 1000.0f), 0, 2) + "s"; + break; + } + case EFFECT_TYPE_POISON: + { + sStats = "Poison: " + Get2DAStrRef("poison", "Name", GetEffectInteger(eEffect, 0)); + break; + } + case EFFECT_TYPE_DISEASE: + { + sStats = "Disease: " + Get2DAStrRef("disease", "Name", GetEffectInteger(eEffect, 0)); + break; + } + case EFFECT_TYPE_CURSE: + { + int nAbility; + string sAbilityDecrease; + for (nAbility = 0; nAbility < 6; nAbility++) + { + int nAbilityMod = GetEffectInteger(eEffect, nAbility); + if (nAbilityMod > 0) + { + string sAbility = GetStringLeft(AbilityToString(nAbility), 3); + sAbilityDecrease += "-" + IntToString(nAbilityMod) + " " + sAbility + ", "; + } + } + sAbilityDecrease = GetStringLeft(sAbilityDecrease, GetStringLength(sAbilityDecrease) - 2); + sStats = sAbilityDecrease; + break; + } + case EFFECT_TYPE_MOVEMENT_SPEED_INCREASE: + case EFFECT_TYPE_MOVEMENT_SPEED_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_MOVEMENT_SPEED_INCREASE, EFFECT_TYPE_MOVEMENT_SPEED_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) + "% Movement Speed"; + break; + } + case EFFECT_TYPE_ELEMENTALSHIELD: + { + sStats = IntToString(GetEffectInteger(eEffect, 0)) + " + " + Get2DAStrRef("iprp_damagecost", "Name", GetEffectInteger(eEffect, 1)) + " (" + DamageTypeToString(GetEffectInteger(eEffect, 2)) + ")"; + break; + } + case EFFECT_TYPE_NEGATIVELEVEL: + { + sStats = "-" + IntToString(GetEffectInteger(eEffect, 0)) + " Levels"; + break; + } + case EFFECT_TYPE_CONCEALMENT: + { + string sMissChance = MissChanceToString(GetEffectInteger(eEffect, 4) - 1); + sStats = IntToString(GetEffectInteger(eEffect, 0)) + "% Concealment" + (sMissChance == "" ? "" : " (" + sMissChance + ")"); + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 1), GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3)); + break; + } + case EFFECT_TYPE_SPELL_RESISTANCE_INCREASE: + case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE: + { + string sModifier = GetModifierType(nEffectType, EFFECT_TYPE_SPELL_RESISTANCE_INCREASE, EFFECT_TYPE_SPELL_RESISTANCE_DECREASE); + sStats = sModifier + IntToString(GetEffectInteger(eEffect, 0)) + " Spell Resistance"; + break; + } + case EFFECT_TYPE_SPELL_FAILURE: + { + sStats = IntToString(GetEffectInteger(eEffect, 0)) + "% Spell Failure (Spell School: " + SpellSchoolToString(GetEffectInteger(eEffect, 1)) + ")"; + break; + } + case EFFECT_TYPE_INVISIBILITY: + { + int nInvisibilityType = GetEffectInteger(eEffect, 0); + if (nEffectIconID == EFFECT_ICON_INVISIBILITY) + bSkipDisplay = nInvisibilityType != INVISIBILITY_TYPE_NORMAL; + else if (nEffectIconID == EFFECT_ICON_IMPROVEDINVISIBILITY) + bSkipDisplay = nInvisibilityType != INVISIBILITY_TYPE_IMPROVED; + if (!bSkipDisplay) + { + sStats = (nInvisibilityType == INVISIBILITY_TYPE_IMPROVED ? "Improved " : "") + "Invisibility"; + sRacialTypeAlignment = GetVersusRacialTypeAndAlignment(GetEffectInteger(eEffect, 1), GetEffectInteger(eEffect, 2), GetEffectInteger(eEffect, 3)); + } + break; + } + case EFFECT_TYPE_HASTE: + { + sStats = "Hasted"; + } + } + if(!bSkipDisplay) + { + sText = sSpellName + " " + sDurationRemaining + (sStats == "" ? "" : " -> " + sStats + sRacialTypeAlignment); + if(sText != "") + { + jRow = CreateLabel(JsonArray(), " " + sText, "lbl_buff_info" + IntToString(nIndex), 700.0f, 10.0f, NUI_HALIGN_LEFT, NUI_VALIGN_TOP, 0.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 10.0; + object oSource = GetEffectCreator(eEffect); + if(GetIsObjectValid(oSource)) + { + sText = GetObjectType(oSource) ? GetName(oSource) : ""; + sText = " Creator: " + sText; + float fLength = IntToFloat(GetStringLength(sText) * 8); + jRow = CreateLabel(JsonArray(), sText, "lbl_buff_source" + IntToString(nIndex), fLength, 15.0f, NUI_HALIGN_LEFT, NUI_VALIGN_BOTTOM, 0.0); + if(oSource == oPlayer) + { + CreateButton(jRow, "Remove", "btn_remove_effect_" + IntToString(nEffectIndex++), 70.0f, 20.0f, 0.0); + jEffectID = JsonArrayInsert(jEffectID, JsonString(GetEffectLinkId(eEffect))); + fHeight += 20.0; + } + else fHeight += 15.0; + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } + } + } + } + nIndex++; + eEffect = GetNextEffect(oPlayer); + } + float fScale = IntToFloat(GetPlayerDeviceProperty(oPlayer, PLAYER_DEVICE_PROPERTY_GUI_SCALE)) / 100.0; + float fX = IntToFloat(GetPlayerDeviceProperty(oPlayer, PLAYER_DEVICE_PROPERTY_GUI_WIDTH)); + fX = fX - (700.0 * fScale); + float fY = 50 * fScale; + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + int nToken = SetWindow(oPlayer, jLayout, AI_EFFECT_ICON_NUI, "Effect Icon Menu", + fX, fY, 700.0, fHeight * fScale, FALSE, FALSE, FALSE, TRUE, FALSE, "0e_nui"); + // Save the associate to the nui for use in 0e_nui + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oPlayer))); + jData = JsonArrayInsert(jData, JsonInt(nEffectIconID)); + jData = JsonArrayInsert(jData, jEffectID); + NuiSetUserData(oPlayer, nToken, jData); + NuiSetBind(oPlayer, nToken, "lbl_buff_name_event", JsonBool(TRUE)); + while(nIndex >= 0) + { + NuiSetBind(oPlayer, nToken, "lbl_buff_info" + IntToString(nIndex) + "_event", JsonBool(TRUE)); + NuiSetBind(oPlayer, nToken, "lbl_buff_source" + IntToString(nIndex) + "_event", JsonBool(TRUE)); + nIndex--; + } + while(nEffectIndex >= 0) + { + NuiSetBind(oPlayer, nToken, "btn_remove_effect_" + IntToString(nEffectIndex) + "_event", JsonBool(TRUE)); + NuiSetBind(oPlayer, nToken, "btn_remove_effect_" + IntToString(nEffectIndex), JsonInt(TRUE)); + nEffectIndex--; + } +} diff --git a/_module/nss/0i_items.nss b/_module/nss/0i_items.nss new file mode 100644 index 00000000..87d3ce7e --- /dev/null +++ b/_module/nss/0i_items.nss @@ -0,0 +1,1243 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +Script Name: 0i_items +Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Include scripts for use with items. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +//#include "0i_main" +#include "0i_messages" +// Returns TRUE if oItem is a weapon. +int ai_GetIsWeapon(object oItem); +// Returns TRUE if oItem is a melee weapon. +int ai_GetIsMeleeWeapon(object oItem); +// Returns TRUE if oItem is a slashing weapon. +int ai_GetIsSlashingWeapon(object oItem); +// Returns TRUE if oItem is a piercing weapon. +int ai_GetIsPiercingWeapon(object oItem); +// Returns TRUE if oItem is a bludgeoning weapon. +int ai_GetIsBludgeoningWeapon(object oItem); +// Returns TRUE if oItem is an ammo. +int ai_GetIsAmmo(object oItem); +// Returns TRUE if oItem is a thrown weapon. +int ai_GetIsThrownWeapon(object oItem); +// Returns TRUE if oItem is able to be used single handed by oCreature. +int ai_GetIsSingleHandedWeapon(object oItem, object oCreature); +// Returns TRUE if oItem is a light weapon for oCreature. +int ai_GetIsLightWeapon(object oItem, object oCreature); +// Returns TRUE if oItem is able to be used two handed by oCreature. +int ai_GetIsTwoHandedWeapon(object oItem, object oCreature); +// Returns TRUE if oItem is a double weapon. +int ai_GetIsDoubleWeapon(object oItem); +// Returns TRUE if oCreature has a ranged weapon equiped and has ammo for it. +int ai_HasRangedWeaponWithAmmo(object oCreature); +// Returns TRUE if oItem is a ranged weapon. +int ai_GetIsRangeWeapon(object oItem); +// Returns the amount of damage the weapon oCreature is holding. +// nDamageAmount tells the function the amount of damage to return; +// 1 - Minimum, 2- Average, 3 Maximum. +// bMelee If it is not a melee weapon then return 0; +int ai_GetWeaponDamage(object oCreature, int nDamageAmount = 3, int bMelee = FALSE); +// Returns TRUE if oItem is a shield. +int ai_GetIsShield(object oItem); +// Returns the size of oItem using 1 = small to 6 = large. +int ai_GetItemSize(object oItem); +// Returns TRUE if the caller has a potion that is identified of nSpell. +int ai_CheckPotionIsIdentified(object oCreature, int nSpell); +// Returns an item from oCreature's inventory with sTag. +// bCheckEquiped will also look through the creatures equiped items. +// Returns OBJECT_INVALID if the items does not exist with sTag. +object ai_GetCreatureHasItem(object oCreature, string sTag, int bCheckEquiped = FALSE); +// Returns TRUE if oCreature can identify oItem based on the file SkillVsItemCost.2da +// Reports the findings to oPC unless oPC = OBJECT_INVALID. +// If the item can be identified by oCreature then it will be identified. +int ai_IdentifyItemVsKnowledge(object oCreature, object oItem, object oPC = OBJECT_INVALID); +// Identifies all items on oObject based on the file SkillVsItemCost.2da +// Reports the findings to oPC unless oPC = OBJECT_INVALID +// bIdentifyAll ignores the chart and does what it says! +void ai_IdentifyAllVsKnowledge(object oCreature, object oContainer, object oPC = OBJECT_INVALID); +// Will (Un)Identify all items on oCreature. +// If bIdentify is TRUE they will all be Identified, FALSE Unidentifies them. +void ai_SetIdentifyAllItems(object oCreature, int bIdentify = TRUE); +// Returns oWeapons attack bonus from either Enhancment or Attack bonus. +int ai_GetWeaponAtkBonus(object oWeapon); +// Returns oArmors armor bonus. +int ai_GetArmorBonus(object oArmor); +// Returns the maximum gold value that an item can have to be equiped. +int ai_GetMaxItemValueThatCanBeEquiped(int nLevel); +// Returns the minimum level that is required to equip this item. +int ai_GetMinimumEquipLevel(object oItem); +// Returns oCreatures total attack bonus with melee weapon (Mostly). +int ai_GetCreatureAttackBonus(object oCreature); +// Returns TRUE if oCreature can use oItem based on Class, Race, and Alignment +// restrictions. Also checks UseMagicDevice of oCreature. +int ai_CheckIfCanUseItem(object oCreature, object oItem); +// Returns TRUE if oCreature can use oItem due to feats. +int ai_GetIsProficientWith(object oCreature, object oItem); +// Gets the Average Damage on the weapon for Main and Off Hand to allow +// us to check which weapon is better for oCreature to equip. +// b2Handed set to TRUE returns only checks main avg damage. +// bOffHand set to TRUE returns the OffHand avg damage. +// if b2Handed & bOffHand are set to TRUE it returns main & offhand added together. +// if oOffWeapon is Set then it will return the Avg Damage assuming oItem is +// the Main weapon and oOffWeapon is in the Offhand. +float ai_GetMeleeWeaponAvgDmg(object oCreature, object oItem, int b2Handed = FALSE, int bOffHand = FALSE, object oOffWeapon = OBJECT_INVALID); +// Sets shield AC on the shield to allow us to check which shield is better +// for oCreature to equip. +int ai_SetShieldAC(object oCreature, object oItem); +// Returns TRUE if oItem has nItemPropertyType. +// nItemPropertySubType will not be used if its below 0. +int ai_GetHasItemProperty(object oItem, int nItemPropertyType, int nItemPropertySubType = -1); +// Returns the highest bonus Lock Picks needed to unlock nLockDC in oCreatures inventory. +object ai_GetBestPicks(object oCreature, int nLockDC); +// Removes all items from oCreature. +void ai_RemoveInventory(object oCreature); +// Copies all equiped and inventory items from oOldHenchman to oNewHenchman. +void ai_MoveInventory(object oOldHenchman, object oNewHenchman); +// Returns if oCreature is proficient with nBaseItem. +// PRC lets the creature use any weapon, but gives -4 penalty if not proficient. +int prc_IsProficient(object oCreature, int nBaseItem); + +int ai_GetIsWeapon(object oItem) +{ + int nType = GetBaseItemType(oItem); + int nWeaponType = StringToInt(Get2DAString("baseitems", "WeaponType", nType)); + if(nWeaponType) return TRUE; + return FALSE; +} +int ai_GetIsMeleeWeapon(object oItem) +{ + int nType = GetBaseItemType(oItem); + if(StringToInt(Get2DAString("baseitems", "WeaponType", nType)) > 0) + { + if(StringToInt(Get2DAString("baseitems", "RangedWeapon", nType)) == 0) return TRUE; + } + return FALSE; +} +int ai_GetIsSingleHandedWeapon(object oItem, object oCreature) +{ + if(!ai_GetIsMeleeWeapon(oItem)) return FALSE; + int nBaseItemType = GetBaseItemType(oItem); + // Weapon Size in the baseitems.2da is 1 = Tiny, 2 = Small, 3 = Medium, 4 = Large. + int nWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nBaseItemType)); + // Creature size is 1 = Tiny, 2 = Small, 3 = Medium, 4 = Large. + int nCreatureSize = GetCreatureSize(oCreature); + return nWeaponSize <= nCreatureSize; +} +int ai_GetIsLightWeapon(object oItem, object oCreature) +{ + if(!ai_GetIsMeleeWeapon(oItem)) return FALSE; + int nBaseItemType = GetBaseItemType(oItem); + // Weapon Size in the baseitems.2da is 1 = Tiny, 2 = Small, 3 = Medium, 4 = Large. + int nWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nBaseItemType)); + // Creature size is 1 = Tiny, 2 = Small, 3 = Medium, 4 = Large. + int nCreatureSize = GetCreatureSize(oCreature); + return nWeaponSize < nCreatureSize; +} +int ai_GetIsTwoHandedWeapon(object oItem, object oCreature) +{ + if(!ai_GetIsMeleeWeapon(oItem)) return FALSE; + int nBaseItemType = GetBaseItemType(oItem); + // Weapon Size in the baseitems.2da is 1 = Tiny, 2 = Small, 3 = Medium, 4 = Large. + int nWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nBaseItemType)); + // Ranged weapons have a value greater than 0 in this field. So melee weapons have 0. + int nWeaponMelee = StringToInt(Get2DAString("baseitems", "RangedWeapon", nBaseItemType)); + // Creature size is 1 = Tiny, 2 = Small, 3 = Medium, 4 = Large. + int nCreatureSize = GetCreatureSize(oCreature); + return (nWeaponMelee == 0 && nWeaponSize > nCreatureSize); +} +int ai_GetIsDoubleWeapon(object oItem) +{ + int iType = GetBaseItemType(oItem); + switch(iType) + { + case BASE_ITEM_DIREMACE: + case BASE_ITEM_DOUBLEAXE: + case BASE_ITEM_TWOBLADEDSWORD: return TRUE; + } + return FALSE; +} +int ai_GetIsSlashingWeapon(object oItem) +{ + int iBaseItemType = GetBaseItemType(oItem); + int iWeaponType = StringToInt(Get2DAString("baseitems", "WeaponType", iBaseItemType)); + // Weapon Type in the baseitems.2da is 1 = Piercing, 2 = Bludgeoning, 3 = Slashing. + return (iWeaponType == 3); +} +int ai_GetIsPiercingWeapon(object oItem) +{ + int iBaseItemType = GetBaseItemType(oItem); + int iWeaponType = StringToInt(Get2DAString("baseitems", "WeaponType", iBaseItemType)); + // Weapon Type in the baseitems.2da is 1 = Piercing, 2 = Bludgeoning, 3 = Slashing. + return (iWeaponType == 1); +} +int ai_GetIsBludgeoningWeapon(object oItem) +{ + int iBaseItemType = GetBaseItemType(oItem); + int iWeaponType = StringToInt(Get2DAString("baseitems", "WeaponType", iBaseItemType)); + // Weapon Type in the baseitems.2da is 1 = Piercing, 2 = Bludgeoning, 3 = Slashing. + return (iWeaponType == 2); +} +int ai_GetIsAmmo(object oItem) +{ + switch(GetBaseItemType(oItem)) + { + case BASE_ITEM_ARROW: return TRUE; + case BASE_ITEM_BOLT: return TRUE; + case BASE_ITEM_BULLET: return TRUE; + } + return FALSE; +} +int ai_GetIsThrownWeapon(object oItem) +{ + switch(GetBaseItemType(oItem)) + { + case BASE_ITEM_DART: return TRUE; + case BASE_ITEM_SHURIKEN: return TRUE; + case BASE_ITEM_THROWINGAXE: return TRUE; + } + return FALSE; +} +int ai_HasRangedWeaponWithAmmo(object oCreature) +{ + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + if(!GetWeaponRanged(oWeapon)) return FALSE; + int nAmmoType, nWeaponType = GetBaseItemType(oWeapon); + object oAmmo = OBJECT_INVALID; + if(nWeaponType == BASE_ITEM_LONGBOW || nWeaponType == BASE_ITEM_SHORTBOW) + { + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_UNLIMITED_AMMUNITION)) return TRUE; + if(GetItemInSlot(INVENTORY_SLOT_ARROWS, oCreature) != OBJECT_INVALID) return TRUE; + nAmmoType = BASE_ITEM_ARROW; + } + else if(nWeaponType == BASE_ITEM_LIGHTCROSSBOW || nWeaponType == BASE_ITEM_HEAVYCROSSBOW) + { + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_UNLIMITED_AMMUNITION)) return TRUE; + if(GetItemInSlot(INVENTORY_SLOT_BOLTS, oCreature) != OBJECT_INVALID) return TRUE; + nAmmoType = BASE_ITEM_BOLT; + } + else if(nWeaponType == BASE_ITEM_SLING) + { + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_UNLIMITED_AMMUNITION)) return TRUE; + if(GetItemInSlot(INVENTORY_SLOT_BULLETS, oCreature) != OBJECT_INVALID) return TRUE; + nAmmoType = BASE_ITEM_BULLET; + } + else if(nWeaponType == BASE_ITEM_THROWINGAXE) return TRUE; + else if(nWeaponType == BASE_ITEM_SHURIKEN) return TRUE; + else if(nWeaponType == BASE_ITEM_DART) return TRUE; + // They don't have any ammo in the slot, but do they have ammo in the inventory? + oAmmo = GetFirstItemInInventory(oCreature); + while(oAmmo != OBJECT_INVALID) + { + if(GetBaseItemType(oAmmo) == nAmmoType) + { + if(nAmmoType == BASE_ITEM_ARROW) ActionEquipItem(oAmmo, INVENTORY_SLOT_ARROWS); + else if(nAmmoType == BASE_ITEM_BOLT) ActionEquipItem(oAmmo, INVENTORY_SLOT_BOLTS); + else if(nAmmoType == BASE_ITEM_BULLET) ActionEquipItem(oAmmo, INVENTORY_SLOT_BULLETS); + return TRUE; + } + oAmmo = GetNextItemInInventory(oCreature); + } + //ai_Debug("0i_items", "254", "They are out of ammo!"); + return FALSE; +} +int ai_GetIsRangeWeapon(object oItem) +{ + switch(GetBaseItemType(oItem)) + { + case BASE_ITEM_DART: return TRUE; + case BASE_ITEM_HEAVYCROSSBOW: return TRUE; + case BASE_ITEM_LIGHTCROSSBOW: return TRUE; + case BASE_ITEM_LONGBOW: return TRUE; + case BASE_ITEM_SHORTBOW: return TRUE; + case BASE_ITEM_SHURIKEN: return TRUE; + case BASE_ITEM_SLING: return TRUE; + case BASE_ITEM_THROWINGAXE: return TRUE; + } + return FALSE; +} +int ai_GetIsFinesseWeapon(object oCreature, object oItem) +{ + switch(GetBaseItemType(oItem)) + { + case BASE_ITEM_DAGGER: return TRUE; + case BASE_ITEM_HANDAXE: return TRUE; + case BASE_ITEM_KAMA: return TRUE; + case BASE_ITEM_KUKRI: return TRUE; + case BASE_ITEM_LIGHTHAMMER: return TRUE; + case BASE_ITEM_LIGHTMACE: return TRUE; + case BASE_ITEM_RAPIER: + { + if(GetCreatureSize(oCreature) > CREATURE_SIZE_SMALL) return TRUE; + return FALSE; + } + case BASE_ITEM_SHORTSWORD: return TRUE; + case BASE_ITEM_SICKLE: return TRUE; + case BASE_ITEM_WHIP: return TRUE; + } + return FALSE; +} +int ai_GetWeaponDamage(object oCreature, int nDamageAmount = 3, int bMelee = FALSE) +{ + object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + if(bMelee && ai_GetIsRangeWeapon(oItem)) return 0; + int nWeaponDamage = GetLocalInt(oItem, "AI_WEAPON_DAMAGE"); + if(!nWeaponDamage) + { + if(ai_GetIsMeleeWeapon(oItem)) + { + nWeaponDamage = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + if(ai_GetIsTwoHandedWeapon(oItem, oCreature)) nWeaponDamage += nWeaponDamage / 2; + } + int nWeaponDice = StringToInt(Get2DAString("baseitems", "NumDice", GetBaseItemType(oItem))); + int nWeaponDie = StringToInt(Get2DAString("baseitems", "DieToRoll", GetBaseItemType(oItem))); + if(nDamageAmount == 1) + { + nWeaponDamage += nWeaponDice; + } + else if(nDamageAmount == 2) + { + nWeaponDamage += nWeaponDice * nWeaponDie / 2; + } + else + { + nWeaponDamage += nWeaponDice * nWeaponDie; + } + SetLocalInt(oItem, "AI_WEAPON_DAMAGE", nWeaponDamage); + } + return nWeaponDamage; +} +int ai_GetIsShield(object oItem) +{ + switch(GetBaseItemType(oItem)) + { + case BASE_ITEM_SMALLSHIELD: return TRUE; + case BASE_ITEM_LARGESHIELD: return TRUE; + case BASE_ITEM_TOWERSHIELD: return TRUE; + } + return FALSE; + } +int ai_GetItemSize(object oItem) +{ + int nBaseItemType = GetBaseItemType(oItem); + int nWidth = StringToInt(Get2DAString("baseitems", "InvSlotWidth", nBaseItemType)); + int nHeight = StringToInt(Get2DAString("baseitems", "InvSlotHeight", nBaseItemType)); + return nWidth + nHeight - 1; +} +int ai_CheckPotionIsIdentified(object oCreature, int nSpell) +{ + int nPotionSpell; + itemproperty ipPotion; + object oPotion = GetFirstItemInInventory(oCreature); + while(oPotion != OBJECT_INVALID) + { + if(GetIdentified(oPotion)) + { + ipPotion = GetFirstItemProperty(oPotion); + nPotionSpell = GetItemPropertySubType(ipPotion); + nPotionSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nPotionSpell)); + //ai_Debug("0i_talents", "318", "Potion ID'ed? nSpell: " + IntToString(nSpell) + " nPotionSpell: " + IntToString(nPotionSpell)); + if(nSpell == nPotionSpell) return TRUE; + } + oPotion = GetNextItemInInventory(oCreature); + } + return FALSE; +} +object ai_GetCreatureHasItem(object oCreature, string sTag, int bCheckEquiped = FALSE) +{ + // Cycle through the creatures unequiped items. + object oItem = GetFirstItemInInventory(oCreature); + while(oItem != OBJECT_INVALID) + { + if(GetTag(oItem) == sTag) return oItem; + oItem = GetNextItemInInventory(oCreature); + } + // Should we check the creatures equiped items. + // If we have already found it then stop looking. + int nSlot = 0; + if(bCheckEquiped) + { + // Check all of the creatures slots(0 - 17). + while(nSlot <= 17) + { + oItem = GetItemInSlot(nSlot, oCreature); + if(GetTag(oItem) == sTag) return oItem; + nSlot ++; + } + } + return OBJECT_INVALID; +} +int ai_IdentifyItemVsKnowledge(object oCreature, object oItem, object oPC = OBJECT_INVALID) +{ + if(GetIdentified(oItem)) return FALSE; + int nKnowledge = GetSkillRank(SKILL_LORE, oCreature); + int nItemValue; // gold value of item + string sBaseName; + string sMaxValue = Get2DAString("SkillVsItemCost", "DeviceCostMax", nKnowledge); + int nMaxValue = StringToInt(sMaxValue); + // * Handle overflow(November 2003 - BK) + if(sMaxValue == "") nMaxValue = 0; + // Setting TRUE to get the true value of the item. + SetIdentified(oItem, TRUE); + nItemValue = GetGoldPieceValue(oItem); + if(nMaxValue <= nItemValue) + { + SetIdentified(oItem, FALSE); + if(oPC != OBJECT_INVALID) + { + sBaseName = GetStringByStrRef(StringToInt(Get2DAString("baseitems", "name", GetBaseItemType(oItem)))); + ai_SendMessages(GetName(oCreature) + " cannot identify " + sBaseName, AI_COLOR_RED, oPC); + } + } + else + { + if(oPC != OBJECT_INVALID) ai_SendMessages(GetName(oCreature) + " has identified " + GetName(oItem), AI_COLOR_GREEN, oPC); + return TRUE; + } + return FALSE; +} +void ai_IdentifyAllVsKnowledge(object oCreature, object oContainer, object oPC = OBJECT_INVALID) +{ + // SkillVsItemCost 2da starts 1 at 0 ... go figure! + int nKnowledge = GetSkillRank(SKILL_LORE, oCreature) - 1; + int nItemValue; // gold value of item + string sBaseName; + string sMaxValue = Get2DAString("SkillVsItemCost", "DeviceCostMax", nKnowledge); + int nMaxValue = StringToInt(sMaxValue); + // * Handle overflow(November 2003 - BK) + if(sMaxValue == "") nMaxValue = 0; + object oItem = GetFirstItemInInventory(oContainer); + while(oItem != OBJECT_INVALID) + { + if(!GetIdentified(oItem)) + { + // setting TRUE to get the true value of the item. + SetIdentified(oItem, TRUE); + nItemValue = GetGoldPieceValue(oItem); + if(nMaxValue < nItemValue) + { + SetIdentified(oItem, FALSE); + sBaseName = GetStringByStrRef(StringToInt(Get2DAString("baseitems", "name", GetBaseItemType(oItem)))); + if(oPC != OBJECT_INVALID) ai_SendMessages(GetName(oCreature) + " cannot identify " + sBaseName, AI_COLOR_RED, oPC); + } + else if(oPC != OBJECT_INVALID) ai_SendMessages(GetName(oCreature) + " has identified " + GetName(oItem), AI_COLOR_GREEN, oPC); + } + oItem = GetNextItemInInventory(oContainer); + } +} +void ai_SetIdentifyAllItems(object oCreature, int bIdentify = TRUE) +{ + object oItem = GetFirstItemInInventory(oCreature); + while(oItem != OBJECT_INVALID) + { + if(!GetIdentified(oItem)) SetIdentified(oItem, bIdentify); + oItem = GetNextItemInInventory(oCreature); + } + int nSlot; + oItem = GetItemInSlot(nSlot, oCreature); + while(nSlot < 11) + { + if(!GetIdentified(oItem)) SetIdentified(oItem, bIdentify); + oItem = GetItemInSlot(++nSlot, oCreature); + } +} +int ai_GetWeaponAtkBonus(object oWeapon) +{ + int nCounter = 1, nPropertyType, nBonus; + // Get first property + itemproperty ipProperty = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(ipProperty)) + { + // Check to see if the property type matches. + nPropertyType = GetItemPropertyType(ipProperty); + if(nPropertyType == 6/*ITEMPROPERTY_ENHANCEMENT*/ || + nPropertyType == 56/*ITEMPROPERTY_ATTACKBONUS*/) + { + nBonus += GetItemPropertyCostTableValue(ipProperty); + } + // Get the next property. + ipProperty = GetNextItemProperty(oWeapon); + } + //ai_Debug("0i_items", "438", GetName(oWeapon) + " attack bonus is " + IntToString(nBonus)); + return nBonus; +} +int ai_GetArmorBonus(object oArmor) +{ + int nTorsoValue = GetItemAppearance(oArmor, ITEM_APPR_TYPE_ARMOR_MODEL, ITEM_APPR_ARMOR_MODEL_TORSO); + //ai_Debug("0i_items", "444", "Armor Bonus: " + Get2DAString("parts_chest.2da", "ACBONUS", nTorsoValue)); + return StringToInt(Get2DAString("parts_chest", "ACBONUS", nTorsoValue)); +} +int ai_GetMaxItemValueThatCanBeEquiped(int nLevel) +{ + return StringToInt(Get2DAString("itemvalue", "MAXSINGLEITEMVALUE", nLevel - 1)); +} +int ai_GetMinimumEquipLevel(object oItem) +{ + int nIndex, nUnIdentified; + if(!GetIdentified(oItem)) + { + nUnIdentified = TRUE; + SetIdentified(oItem, TRUE); + } + int nGoldValue = GetGoldPieceValue(oItem); + if(nUnIdentified) SetIdentified(oItem, FALSE); + int n2daMaxRow = Get2DARowCount("itemvalue"); + while(nIndex < n2daMaxRow) + { + if(nGoldValue <= StringToInt(Get2DAString("itemvalue", "MAXSINGLEITEMVALUE", nIndex))) + { + return nIndex + 1; + } + nIndex++; + } + return nIndex; +} +int ai_GetCreatureAttackBonus(object oCreature) +{ + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature); + int nAtkBonus = GetBaseAttackBonus(oCreature); + if((GetHasFeat(FEAT_WEAPON_FINESSE, oCreature) && ai_GetIsFinesseWeapon(oCreature, oWeapon)) || + ai_GetIsRangeWeapon(oWeapon)) + { + nAtkBonus += GetAbilityModifier(ABILITY_DEXTERITY, oCreature); + } + else nAtkBonus += GetAbilityModifier(ABILITY_STRENGTH, oCreature); + if(ai_GetIsMeleeWeapon(oWeapon)) nAtkBonus += ai_GetWeaponAtkBonus(oWeapon); + return nAtkBonus; + } +int ai_CheckUseMagicDevice(object oCreature, string sColumn, object oItem) +{ + if(!GetLocalInt(GetModule(), AI_RULE_ALLOW_UMD)) return FALSE; + int nUMD = GetSkillRank(SKILL_USE_MAGIC_DEVICE, oCreature); + //ai_Debug("0i_talents", "1600", GetName(oCreature) + " is check UMD: " + IntToString(nUMD)); + if(nUMD < 1) return FALSE; + int nDC, nIndex, nItemValue = GetGoldPieceValue(oItem); + while(nIndex < 55) + { + //ai_Debug("0i_talents", "1605", GetName(oItem) + " has a value of " + + // Get2DAString("skillvsitemcost", "DeviceCostMax", nIndex) + + // " nIndex: " + IntToString(nIndex)); + if(nItemValue < StringToInt(Get2DAString("skillvsitemcost", "DeviceCostMax", nIndex))) + { + //ai_Debug("0i_talents", "1610", "nUMD >= " + Get2DAString("skillvsitemcost", sColumn, nIndex)); + if(nUMD >= StringToInt(Get2DAString("skillvsitemcost", sColumn, nIndex))) return TRUE; + return FALSE; + } + nIndex++; + } + return FALSE; +} +int ai_CheckIfCanUseItem(object oCreature, object oItem) +{ + int bAlign, bClass, bRace, bAlignLimit, bClassLimit, bRaceLimit; + int nIprpSubType, nItemPropertyType; + // Check to see if this item is limited to a specific alignment, class, or race. + int nAlign1 = GetAlignmentLawChaos(oCreature); + int nAlign2 = GetAlignmentGoodEvil(oCreature); + int nRace = GetRacialType(oCreature); + //ai_Debug("0i_items", "615", "nAlign1: " + IntToString(nAlign1) + + // " nAlign2: " + IntToString(nAlign2) + " nRace: " + IntToString(nRace)); + itemproperty ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + nItemPropertyType = GetItemPropertyType(ipProp); + //ai_Debug("0i_items", "620", "ItempropertyType(62/63/64/65): " + IntToString(nItemPropertyType)); + if(nItemPropertyType == ITEM_PROPERTY_USE_LIMITATION_ALIGNMENT_GROUP) + { + bAlignLimit = TRUE; + // SubType is the group index for iprp_aligngrp.2da + nIprpSubType = GetItemPropertySubType(ipProp); + //ai_Debug("0i_items", "626", "nIprpSubType: " + IntToString(nIprpSubType)); + if(nIprpSubType == nAlign1 || nIprpSubType == nAlign2) bAlign = TRUE; + } + else if(nItemPropertyType == ITEM_PROPERTY_USE_LIMITATION_SPECIFIC_ALIGNMENT) + { + bAlignLimit = TRUE; + // SubType is the alignment index for iprp_alignment.2da + nIprpSubType = GetItemPropertySubType(ipProp); + //ai_Debug("0i_items", "634", "nIprpSubType: " + IntToString(nIprpSubType)); + if(nIprpSubType == 0 && nAlign1 == 2 && nAlign2 == 4) bAlign = TRUE; + else if(nIprpSubType == 1 && nAlign1 == 2 && nAlign2 == 1) bAlign = TRUE; + else if(nIprpSubType == 2 && nAlign1 == 2 && nAlign2 == 5) bAlign = TRUE; + else if(nIprpSubType == 3 && nAlign1 == 1 && nAlign2 == 4) bAlign = TRUE; + else if(nIprpSubType == 4 && nAlign1 == 1 && nAlign2 == 1) bAlign = TRUE; + else if(nIprpSubType == 5 && nAlign1 == 1 && nAlign2 == 5) bAlign = TRUE; + else if(nIprpSubType == 6 && nAlign1 == 3 && nAlign2 == 4) bAlign = TRUE; + else if(nIprpSubType == 7 && nAlign1 == 3 && nAlign2 == 1) bAlign = TRUE; + else if(nIprpSubType == 8 && nAlign1 == 3 && nAlign2 == 5) bAlign = TRUE; + } + else if(nItemPropertyType == ITEM_PROPERTY_USE_LIMITATION_CLASS) + { + bClassLimit = TRUE; + // SubType is the class index for classes.2da + nIprpSubType = GetItemPropertySubType(ipProp); + //ai_Debug("0i_items", "650", "nIprpSubType: " + IntToString(nIprpSubType)); + int nClassPosition = 1; + int nClass = GetClassByPosition(nClassPosition, oCreature); + while(nClassPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + if(nIprpSubType == nClass) bClass = TRUE; + nClass = GetClassByPosition(++nClassPosition, oCreature); + } + } + else if(nItemPropertyType == ITEM_PROPERTY_USE_LIMITATION_RACIAL_TYPE) + { + bRaceLimit = TRUE; + // SubType is the race index for racialtypes.2da + nIprpSubType = GetItemPropertySubType(ipProp); + //ai_Debug("0i_items", "664", "nIprpSubType: " + IntToString(nIprpSubType)); + if(nIprpSubType == nRace) bRace = TRUE; + } + ipProp = GetNextItemProperty(oItem); + } + //ai_Debug("0i_items", "669", "bAlignLimit: " + IntToString(bAlignLimit) + " bAlign: " + IntToString(bAlign) + + // " bClassLimit: " + IntToString(bClassLimit) + " bClass: " + IntToString(bClass) + + // " bRaceLimit: " + IntToString(bRaceLimit) + " bRace: " + IntToString(bRace)); + if(bClassLimit && !bClass && !ai_CheckUseMagicDevice(oCreature, "SkillReq_Class", oItem)) return FALSE; + if(bRaceLimit && !bRace && !ai_CheckUseMagicDevice(oCreature, "SkillReq_Race", oItem)) return FALSE; + if(bAlignLimit && !bAlign && !ai_CheckUseMagicDevice(oCreature, "SkillReq_Align", oItem)) return FALSE; + return TRUE; +} +int ai_GetIsProficientWith(object oCreature, object oItem) +{ + int nWeaponType = GetBaseItemType(oItem); + // In the PRC you can equip any weapon. + if(GetLocalInt(GetModule(), AI_USING_PRC)) return TRUE; + int nFeat = StringToInt(Get2DAString("baseitems", "ReqFeat0", nWeaponType)); + // If it is 0 then it doesn't require a feat or we are at the end of the + // feat requirements. + if(nFeat == 0) return TRUE; + if(GetHasFeat(nFeat, oCreature)) return TRUE; + nFeat = StringToInt(Get2DAString("baseitems", "ReqFeat1", nWeaponType)); + if(nFeat == 0) return FALSE; + if(GetHasFeat(nFeat, oCreature)) return TRUE; + nFeat = StringToInt(Get2DAString("baseitems", "ReqFeat2", nWeaponType)); + if(nFeat == 0) return FALSE; + if(GetHasFeat(nFeat, oCreature)) return TRUE; + nFeat = StringToInt(Get2DAString("baseitems", "ReqFeat3", nWeaponType)); + if(nFeat == 0) return FALSE; + if(GetHasFeat(nFeat, oCreature)) return TRUE; + nFeat = StringToInt(Get2DAString("baseitems", "ReqFeat4", nWeaponType)); + if(nFeat == 0) return FALSE; + if(GetHasFeat(nFeat, oCreature)) return TRUE; + return FALSE; +} +float ai_GetMeleeWeaponAvgDmg(object oCreature, object oItem, int b2Handed = FALSE, int bOffHand = FALSE, object oOffWeapon = OBJECT_INVALID) +{ + // Has this weapon already been calculated for this creature? + if(oCreature == GetLocalObject(oItem, "AI_CREATURE_POSSESSION")) + { + // Return the Main weapons Avg Damage while using a weapon in the off hand. + if(oOffWeapon != OBJECT_INVALID) + { + // We recalculate all OffWeapon avg damage unless its a double weapon. + if(oOffWeapon == oItem) + { + float fMain2WDmg = GetLocalFloat(oItem, "AI_MAIN_2W_HAND_AVG_DMG"); + // If they passed that this is a 2handed weapon then return the total + // Avg Dmg for oItem. Used for double weapons. + if(b2Handed) + { + fMain2WDmg += ai_GetMeleeWeaponAvgDmg(oCreature, oItem, FALSE, TRUE); + } + if(AI_DEBUG) ai_Debug("0i_items", "611", GetName(oItem) + " avg dmg with Offhand weapon (" + GetName(oOffWeapon) + ") " + FloatToString(fMain2WDmg, 0, 2)); + return fMain2WDmg; + } + } + // Return the avg dmg for oItem assuming it is in the OffHand. + else if(bOffHand) + { + float fOffHandDmg = GetLocalFloat(oItem, "AI_OFFHAND_AVG_DMG"); + if(AI_DEBUG) ai_Debug("0i_items", "618", GetName(oItem) + " fOffHandAvgDmg: " + FloatToString(fOffHandDmg, 0, 2)); + return fOffHandDmg; + } + // If we get here then Return the avg dmg for oItem assuming its in the main hand. + else + { + float fMainDmg = GetLocalFloat(oItem, "AI_AVG_DMG"); + if(AI_DEBUG)ai_Debug("0i_items", "623", GetName(oItem) + " fMainDmg: " + FloatToString(fMainDmg, 0, 2)); + return fMainDmg; + } + } + // Set the creature to this item that we are calculationg the avg damages for. + SetLocalObject(oItem, "AI_CREATURE_POSSESSION", oCreature); + int nItemType = GetBaseItemType(oItem); + // Figure average damage for one attack, or two with two weapons. + // We are keeping it simple to reduce time and checks. + // Get the weapons base stats. + int nMinDmg = StringToInt(Get2DAString("baseitems", "NumDice", nItemType)); + int nMaxDmg = nMinDmg * StringToInt(Get2DAString("baseitems", "DieToRoll", nItemType)); + int nThreat = StringToInt(Get2DAString("baseitems", "CritThreat", nItemType)); + int nMultiplier = StringToInt(Get2DAString("baseitems", "CritHitMult", nItemType)); + int nIndex, nBonusMinDmg, nBonusMaxDmg, nItemPropertyType, nNumDice; + // We set ToHit to 10 for a 50% chance to hit without modifiers. + float fCritBonusDmg, fToHit = 10.0; + if(GetLocalInt(GetModule(), AI_USING_PRC)) + { + if(!prc_IsProficient(oCreature, nItemType)) fToHit -= 4.0; + } + // Check oCreature's feats. + if(GetHasFeat(FEAT_WEAPON_FINESSE, oCreature) && + ai_GetIsLightWeapon(oItem, oCreature)) + { + // Add Dexterity modifier to the Attack bonus. + nIndex = GetAbilityModifier(ABILITY_DEXTERITY, oCreature); + } + else + { + // Add Strength modifier to the attack bonus. + nIndex = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + // Add 1/2 strength modifier to damage for 2handed weapons, but not Double weapons. + if(b2Handed && !bOffHand) + { + nMinDmg += nIndex / 2; + nMaxDmg += nIndex / 2; + } + } + fToHit += nIndex; + if(GetHasFeat(StringToInt(Get2DAString("baseitems", "WeaponFocusFeat", nItemType)), oCreature, TRUE)) + { + fToHit += 1.0; + if(GetHasFeat(StringToInt(Get2DAString("baseitems", "WeaponSpecializationFeat", nItemType)), oCreature, TRUE)) + { + nMinDmg += 2; + nMaxDmg += 2; + } + if(GetHasFeat(StringToInt(Get2DAString("baseitems", "EpicWeaponFocusFeat", nItemType)), oCreature, TRUE)) + { + fToHit += 2.0; + if(GetHasFeat(StringToInt(Get2DAString("baseitems", "EpicWeaponSpecializationFeat", nItemType)), oCreature, TRUE)) + { + nMinDmg += 4; + nMaxDmg += 4; + } + } + } + if(GetHasFeat(StringToInt(Get2DAString("baseitems", "WeaponImprovedCriticalFeat", nItemType)), oCreature, TRUE)) + { + nMultiplier += nMultiplier; + if(GetHasFeat(StringToInt(Get2DAString("baseitems", "EpicWeaponOverwhelmingCriticalFeat", nItemType)), oCreature, TRUE)) + { + if(nMultiplier > 3) fCritBonusDmg = 10.5; + else if(nMultiplier == 3) fCritBonusDmg = 7.0; + else fCritBonusDmg = 3.5; + } + } + // Check oItem's properties. + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + nItemPropertyType = GetItemPropertyType(ipProperty); + if(nItemPropertyType == ITEM_PROPERTY_ENHANCEMENT_BONUS) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + nBonusMinDmg += nIndex; + nBonusMaxDmg += nIndex; + fToHit += IntToFloat(nIndex); + } + else if(nItemPropertyType == ITEM_PROPERTY_DAMAGE_BONUS) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + nNumDice = StringToInt(Get2DAString("iprp_damagecost", "NumDice", nIndex)); + nBonusMinDmg += nNumDice; + nBonusMaxDmg += nNumDice * StringToInt(Get2DAString("iprp_damagecost", "Die", nIndex)); + } + else if(nItemPropertyType == ITEM_PROPERTY_ATTACK_BONUS) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + fToHit += IntToFloat(nIndex); + } + else if(nItemPropertyType == ITEM_PROPERTY_KEEN) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + nMultiplier += nMultiplier; + } + else if(nItemPropertyType == ITEM_PROPERTY_HASTE) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + nMinDmg += nMinDmg; + nMaxDmg += nMaxDmg; + nBonusMinDmg += nBonusMinDmg; + nBonusMaxDmg += nBonusMaxDmg; + nMultiplier += nMultiplier; + } + else if(nItemPropertyType == ITEM_PROPERTY_MASSIVE_CRITICALS) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + nNumDice = StringToInt(Get2DAString("iprp_damagecost", "NumDice", nIndex)); + fCritBonusDmg += IntToFloat(nNumDice) + IntToFloat(nNumDice * StringToInt(Get2DAString("iprp_damagecost", "Die", nIndex))) / 2.0; + } + else if(nItemPropertyType == ITEM_PROPERTY_DECREASED_ENHANCEMENT_MODIFIER) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + nBonusMinDmg -= nIndex; + nBonusMaxDmg -= nIndex; + fToHit -= IntToFloat(nIndex); + } + else if(nItemPropertyType == ITEM_PROPERTY_DECREASED_ATTACK_MODIFIER) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + fToHit -= IntToFloat(nIndex); + } + else if(nItemPropertyType == ITEM_PROPERTY_DECREASED_DAMAGE) + { + nIndex = GetItemPropertyCostTableValue(ipProperty); + nBonusMinDmg -= nIndex; + nBonusMaxDmg -= nIndex; + } + else if(nItemPropertyType == ITEM_PROPERTY_NO_DAMAGE) + { + // A weapon always does a minimum of 1 pnt of damage. + nMinDmg = 1; + nMaxDmg = 1; + } + ipProperty = GetNextItemProperty(oItem); + } + float fAvgDmg = IntToFloat(nMinDmg + nMaxDmg + nBonusMinDmg + nBonusMaxDmg) / 2; + // Set value for Offhand chance to hit. + float fOffHandToHit = fToHit - 10.0; + float fOffHandAvgDmg = fAvgDmg; + // Set value for Main hand chance to hit with a weapon in Off hand. + float fMain2HandToHit = fToHit - 6.0; + float fMain2HandAvgDmg = fAvgDmg; + // Calculate the avg dmg for oItem used in the main hand with no Off hand weapon. + fToHit = fToHit / 20.0; + float fThreatChance = (IntToFloat(nThreat) / 20.0) * fToHit; + fAvgDmg = (fAvgDmg * fToHit) + ((fAvgDmg * IntToFloat(nMultiplier) + fCritBonusDmg) * fThreatChance); + SetLocalFloat(oItem, "AI_AVG_DMG", fAvgDmg); + if(AI_DEBUG) ai_Debug("0i_items", "768", GetName(oItem) + " fSingleAvgDmg: " + FloatToString(fAvgDmg, 0, 2)); + if(!b2Handed || (b2Handed && oOffWeapon != OBJECT_INVALID)) + { + // Calculate chance to hit based on two weapon feats and main hand vs off hand. + if(GetHasFeat(374/*Dual_Wield*/, oCreature)) + { + if(ai_GetArmorBonus(GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature)) < 4) + { + fMain2HandToHit += 2.0; + fOffHandToHit += 6.0; + } + } + else + { + if(GetHasFeat(FEAT_AMBIDEXTERITY, oCreature)) fOffHandToHit += 4.0; + if(GetHasFeat(FEAT_TWO_WEAPON_FIGHTING, oCreature)) + { + fMain2HandToHit += 2.0; + fOffHandToHit += 2.0; + } + } + if(ai_GetIsLightWeapon(oItem, oCreature)) fOffHandToHit += 2.0; + if(oOffWeapon != OBJECT_INVALID && + (ai_GetIsLightWeapon(oOffWeapon, oCreature) || ai_GetIsDoubleWeapon(oItem))) + { + fMain2HandToHit += 2.0; + } + // Calculate the avg dmg for oItem used in the main hand with an off hand weapon. + fMain2HandToHit = fMain2HandToHit / 20.0; + fThreatChance = (IntToFloat(nThreat) / 20.0) * fMain2HandToHit; + fMain2HandAvgDmg = (fMain2HandAvgDmg * fMain2HandToHit) + ((fMain2HandAvgDmg * IntToFloat(nMultiplier) + fCritBonusDmg) * fThreatChance); + SetLocalFloat(oItem, "AI_MAIN_2W_HAND_AVG_DMG", fMain2HandAvgDmg); + if(AI_DEBUG) ai_Debug("0i_items", "768", GetName(oItem) + " fMain2HandAvgDmg: " + FloatToString(fMain2HandAvgDmg, 0, 2)); + // Calculate the avg dmg for oItem used in the off hand. + fOffHandToHit = fOffHandToHit / 20.0; + fThreatChance = (IntToFloat(nThreat) / 20.0) * fOffHandToHit; + fOffHandAvgDmg = (fOffHandAvgDmg * fOffHandToHit) + ((fOffHandAvgDmg * IntToFloat(nMultiplier) + fCritBonusDmg) * fThreatChance); + SetLocalFloat(oItem, "AI_OFFHAND_AVG_DMG", fOffHandAvgDmg); + if(AI_DEBUG) ai_Debug("0i_items", "790", GetName(oItem) + " fOffHandAvgDmg: " + FloatToString(fOffHandAvgDmg, 0, 2)); + // Return the correct value based on params passed. + if(oOffWeapon != OBJECT_INVALID) + { + // This is used only for double weapons! Must pass b2Handed = TRUE and + // oOffWeapon = the double weapon that was passes as oItem. + if(b2Handed) return fMain2HandAvgDmg + fOffHandAvgDmg; + return fMain2HandAvgDmg; + } + if(bOffHand) return fOffHandAvgDmg; + } + return fAvgDmg; +} +int ai_SetShieldAC(object oCreature, object oItem) +{ + if(oCreature == GetLocalObject(oItem, "AI_CREATURE_POSSESSION")) + { + return GetLocalInt(oItem, "AI_SHIELD_AC"); + } + // Set the creature who has this item for setting the power of. + SetLocalObject(oItem, "AI_CREATURE_POSSESSION", oCreature); + int nItemType = GetBaseItemType(oItem); + int nAC, nItemPropertyType; + if(nItemType == BASE_ITEM_SMALLSHIELD) nAC = 1; + else if(nItemType == BASE_ITEM_LARGESHIELD) nAC = 2; + else if(nItemType == BASE_ITEM_TOWERSHIELD) nAC = 3; + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + nItemPropertyType = GetItemPropertyType(ipProperty); + if(nItemPropertyType == ITEM_PROPERTY_AC_BONUS) + { + nAC += GetItemPropertyCostTableValue(ipProperty); + } + else if(nItemPropertyType == ITEM_PROPERTY_DECREASED_AC) + { + nAC -= GetItemPropertyCostTableValue(ipProperty); + } + else if(nItemPropertyType == ITEM_PROPERTY_HASTE) + { + nAC += 4; + } + ipProperty = GetNextItemProperty(oItem); + } + SetLocalInt(oItem, "AI_SHIELD_AC", nAC); + if(AI_DEBUG) ai_Debug("0i_items", "718", GetName(oItem) + " nAC: " + IntToString(nAC)); + return nAC; +} +int ai_GetHasItemProperty(object oItem, int nItemPropertyType, int nItemPropertySubType = -1) +{ + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + if(GetItemPropertyType(ipProperty) == nItemPropertyType) + { + if(nItemPropertySubType > -1) + { + if(GetItemPropertySubType(ipProperty) == nItemPropertySubType) return TRUE; + } + else return TRUE; + } + ipProperty = GetNextItemProperty(oItem); + } + return FALSE; +} +object ai_GetBestPicks(object oCreature, int nLockDC) +{ + int nSkill = GetSkillRank(SKILL_OPEN_LOCK, oCreature); + int nBonus, nBestBonus = 99, nNeededBonus = nLockDC - nSkill - 20; + //ai_Debug("0i_items", "651", "nNeededBonus: " + IntToString(nNeededBonus)); + // We don't need to use any picks! + if(nNeededBonus < 1) return OBJECT_INVALID; + object oBestItem = OBJECT_INVALID; + object oItem = GetFirstItemInInventory(oCreature); + while(oItem != OBJECT_INVALID) + { + if(GetBaseItemType(oItem) == BASE_ITEM_THIEVESTOOLS) + { + // Get the tools bonus. + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + if(GetItemPropertyType(ipProperty) == ITEM_PROPERTY_THIEVES_TOOLS) + { + nBonus = GetItemPropertyCostTableValue(ipProperty); + if(nBonus >= nNeededBonus && nBonus < nBestBonus) + { + nBestBonus = nBonus; + oBestItem = oItem; + SetLocalInt(oBestItem, "AI_BONUS", nBestBonus); + break; + } + } + ipProperty = GetNextItemProperty(oItem); + } + } + oItem = GetNextItemInInventory(oCreature); + } + return oBestItem; +} +void ai_RemoveInventory(object oCreature) +{ + object oItem = GetFirstItemInInventory(oCreature); + while(oItem != OBJECT_INVALID) + { + DestroyObject(oItem); + oItem = GetNextItemInInventory(oCreature); + } + int nIndex; + for(nIndex = 0; nIndex <= 13; nIndex++) + { + oItem = GetItemInSlot(nIndex, oCreature); + DestroyObject(oItem); + } +} +void ai_MoveInventory(object oOldHenchman, object oNewHenchman) +{ + // Move all inventory items. + object oItem = GetFirstItemInInventory(oOldHenchman); + while(oItem != OBJECT_INVALID) + { + CopyItem(oItem, oNewHenchman, TRUE); + oItem = GetNextItemInInventory(oOldHenchman); + } + // Move all equiped items and equip on oNewHenchman. + int nIndex; + object oNewItem; + for(nIndex = 0; nIndex <= 13; nIndex++) + { + oItem = GetItemInSlot(nIndex, oOldHenchman); + if(oItem != OBJECT_INVALID) + { + oNewItem = CopyItem(oItem, oNewHenchman, TRUE); + if(!GetIdentified(oNewItem)) SetIdentified(oNewItem, TRUE); + ActionEquipItem(oNewItem, nIndex); + } + } +} +int prc_IsProficient(object oCreature, int nBaseItem) +{ + switch(nBaseItem) + { + //special case: counts as simple for chitine + case BASE_ITEM_SHORTSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(3600/*FEAT_MINDBLADE*/, oCreature) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + && GetRacialType(oCreature) == 76/*RACIAL_TYPE_CHITINE*/) + || GetHasFeat(7901/*FEAT_WEAPON_PROFICIENCY_SHORTSWORD*/, oCreature); + + case BASE_ITEM_LONGSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(3600/*FEAT_MINDBLADE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oCreature) + || GetHasFeat(7902/*FEAT_WEAPON_PROFICIENCY_LONGSWORD*/, oCreature); + + case BASE_ITEM_BATTLEAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + && GetRacialType(oCreature) == 216/*RACIAL_TYPE_GNOLL*/) + || GetHasFeat(7903/*FEAT_WEAPON_PROFICIENCY_BATTLEAXE*/, oCreature); + + case BASE_ITEM_BASTARDSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature) + || GetHasFeat(3600/*FEAT_MINDBLADE*/, oCreature) + || GetHasFeat(7904/*FEAT_WEAPON_PROFICIENCY_BASTARD_SWORD*/, oCreature); + + case BASE_ITEM_LIGHTFLAIL: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(7905/*FEAT_WEAPON_PROFICIENCY_LIGHT_FLAIL*/, oCreature); + + case BASE_ITEM_WARHAMMER: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(7906/*FEAT_WEAPON_PROFICIENCY_WARHAMMER*/, oCreature); + + case BASE_ITEM_LONGBOW: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oCreature) + || GetHasFeat(7907/*FEAT_WEAPON_PROFICIENCY_LONGBOW*/, oCreature); + + case BASE_ITEM_LIGHTMACE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(7908/*FEAT_WEAPON_PROFICIENCY_LIGHT_MACE*/, oCreature); + + case BASE_ITEM_HALBERD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(7909/*FEAT_WEAPON_PROFICIENCY_HALBERD*/, oCreature); + + case BASE_ITEM_SHORTBOW: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oCreature) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + && GetRacialType(oCreature) == 216/*RACIAL_TYPE_GNOLL*/) + || GetHasFeat(7910/*FEAT_WEAPON_PROFICIENCY_SHORTBOW*/, oCreature); + + case BASE_ITEM_TWOBLADEDSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature) + || GetHasFeat(7911/*FEAT_WEAPON_PROFICIENCY_TWO_BLADED_SWORD*/, oCreature); + + case BASE_ITEM_GREATSWORD: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(7912/*FEAT_WEAPON_PROFICIENCY_GREATSWORD*/, oCreature); + + case BASE_ITEM_GREATAXE: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(7913/*FEAT_WEAPON_PROFICIENCY_GREATAXE*/, oCreature); + + case BASE_ITEM_DART: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oCreature) + || GetHasFeat(7914/*FEAT_WEAPON_PROFICIENCY_DART*/, oCreature); + + case BASE_ITEM_DIREMACE: + return GetHasFeat(7915/*FEAT_WEAPON_PROFICIENCY_DIRE_MACE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case BASE_ITEM_DOUBLEAXE: + return GetHasFeat(7916/*FEAT_WEAPON_PROFICIENCY_DOUBLE_AXE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case BASE_ITEM_HEAVYFLAIL: + return GetHasFeat(7917/*FEAT_WEAPON_PROFICIENCY_HEAVY_FLAIL*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + case BASE_ITEM_LIGHTHAMMER: + return GetHasFeat(7918/*FEAT_WEAPON_PROFICIENCY_LIGHT_HAMMER*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + case BASE_ITEM_HANDAXE: + return GetHasFeat(7919/*FEAT_WEAPON_PROFICIENCY_HANDAXE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oCreature); + + case BASE_ITEM_KAMA: + return GetHasFeat(7920/*FEAT_WEAPON_PROFICIENCY_KAMA*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case BASE_ITEM_KATANA: + return GetHasFeat(7921/*FEAT_WEAPON_PROFICIENCY_KATANA*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case BASE_ITEM_KUKRI: + return GetHasFeat(7922/*FEAT_WEAPON_PROFICIENCY_KUKRI*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case BASE_ITEM_MORNINGSTAR: + return GetHasFeat(7923/*FEAT_WEAPON_PROFICIENCY_MORNINGSTAR*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature); + + case BASE_ITEM_QUARTERSTAFF: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_WIZARD, oCreature); + + case BASE_ITEM_RAPIER: + return GetHasFeat(7924/*FEAT_WEAPON_PROFICIENCY_RAPIER*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ELF, oCreature); + + case BASE_ITEM_SCIMITAR: + return GetHasFeat(7925/*FEAT_WEAPON_PROFICIENCY_SCIMITAR*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oCreature); + + case BASE_ITEM_SCYTHE: + return GetHasFeat(7926/*FEAT_WEAPON_PROFICIENCY_SCYTHE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + case BASE_ITEM_SHORTSPEAR: + return GetHasFeat(7927/*FEAT_WEAPON_PROFICIENCY_SHORTSPEAR*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oCreature); + + case BASE_ITEM_SHURIKEN: + return GetHasFeat(7928/*FEAT_WEAPON_PROFICIENCY_SHURIKEN*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oCreature); + + case BASE_ITEM_SICKLE: + return GetHasFeat(7929/*FEAT_WEAPON_PROFICIENCY_SICKLE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oCreature); + + case BASE_ITEM_SLING: + return GetHasFeat(7930/*FEAT_WEAPON_PROFICIENCY_SLING*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oCreature); + + case BASE_ITEM_THROWINGAXE: + return GetHasFeat(7931/*FEAT_WEAPON_PROFICIENCY_THROWING_AXE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + || GetHasFeat(3600/*FEAT_MINDBLADE*/, oCreature); + + case BASE_ITEM_CSLASHWEAPON: + case BASE_ITEM_CPIERCWEAPON: + case BASE_ITEM_CBLUDGWEAPON: + case BASE_ITEM_CSLSHPRCWEAP: + return GetHasFeat(FEAT_WEAPON_PROFICIENCY_CREATURE, oCreature); + + case BASE_ITEM_TRIDENT: + return GetHasFeat(7932/*FEAT_WEAPON_PROFICIENCY_TRIDENT*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_DRUID, oCreature); + + case 124://BASE_ITEM_DOUBLE_SCIMITAR: + return GetHasFeat(7948/*FEAT_WEAPON_PROFICIENCY_DOUBLE_SCIMITAR*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case 119://BASE_ITEM_FALCHION: + return GetHasFeat(7943/*FEAT_WEAPON_PROFICIENCY_FALCHION*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + case 125://BASE_ITEM_GOAD: + return GetHasFeat(7949/*FEAT_WEAPON_PROFICIENCY_GOAD*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature); + + case 122://BASE_ITEM_HEAVY_MACE: + return GetHasFeat(7946/*FEAT_WEAPON_PROFICIENCY_HEAVY_MACE*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_SIMPLE, oCreature); + + case 115://BASE_ITEM_HEAVY_PICK: + return GetHasFeat(7939/*FEAT_WEAPON_PROFICIENCY_HEAVY_PICK*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + case 116://BASE_ITEM_LIGHT_PICK: + return GetHasFeat(7940/*FEAT_WEAPON_PROFICIENCY_LIGHT_PICK*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + case 121://BASE_ITEM_KATAR: + return GetHasFeat(7945/*FEAT_WEAPON_PROFICIENCY_KATAR*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case 123://BASE_ITEM_MAUL: + return GetHasFeat(7947/*FEAT_WEAPON_PROFICIENCY_MAUL*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + case 118://BASE_ITEM_NUNCHAKU: + return GetHasFeat(7942/*FEAT_WEAPON_PROFICIENCY_NUNCHAKU*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case 117://BASE_ITEM_SAI: + return GetHasFeat(7941/*FEAT_WEAPON_PROFICIENCY_SAI*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MONK, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case 120://BASE_ITEM_SAP: + return GetHasFeat(7944/*FEAT_WEAPON_PROFICIENCY_SAP*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_ROGUE, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature); + + //special case: counts as martial for dwarves + case BASE_ITEM_DWARVENWARAXE: + return GetHasFeat(7933/*FEAT_WEAPON_PROFICIENCY_DWARVEN_WARAXE*/, oCreature) + || (GetHasFeat(FEAT_WEAPON_PROFICIENCY_MARTIAL, oCreature) + && GetHasFeat(4710/*FEAT_DWARVEN*/, oCreature)) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + + case BASE_ITEM_WHIP: + return GetHasFeat(7934/*FEAT_WEAPON_PROFICIENCY_WHIP*/, oCreature) + || GetHasFeat(FEAT_WEAPON_PROFICIENCY_EXOTIC, oCreature); + } + return TRUE; +} +int ai_GetItemUses(object oItem, int nItemPropertySubType) +{ + int nUses; + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + if(GetItemPropertyType(ipProperty) == ITEM_PROPERTY_HEALERS_KIT) return GetItemStackSize(oItem); + if(nItemPropertySubType > -1) + { + if(GetItemPropertySubType(ipProperty) == nItemPropertySubType) + { + // Get how they use the item (charges or uses per day). + nUses = GetItemPropertyCostTableValue(ipProperty); + if(nUses == 1) return GetItemStackSize(oItem); + else if(nUses > 1 && nUses < 7) return GetItemCharges(oItem); + else if(nUses == 7 || nUses == 13) return 999; + else if(nUses > 7 && nUses < 13) return GetItemPropertyUsesPerDayRemaining(oItem, ipProperty); + } + } + else return TRUE; + ipProperty = GetNextItemProperty(oItem); + } + return FALSE; +} + diff --git a/_module/nss/0i_main.nss b/_module/nss/0i_main.nss new file mode 100644 index 00000000..e32aa716 --- /dev/null +++ b/_module/nss/0i_main.nss @@ -0,0 +1,1347 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_main +//////////////////////////////////////////////////////////////////////////////// + Include script for handling main/basic functions not defined by other includes. + + Database structure: Json with indexes. + name (string) - The associatetype to link the data. "pc", "familiar", etc. + modes (jsonarray) - 0-aimodes (int), 1-magicmodes (int) + buttons (jsonarray) - 0-widgetbuttons (int), 1-aibuttons (int) + aidata (jsonarray) - 0-difficulty (int), 1-healoutcombat (int), 2-healincombat (int), + 3-lootrange (float), 4-lockrange (float), 5-traprange (float), + 6-Follow range (float). + lootfilters (jsonarray) - 0-maxweight (int), 1-lootfilters (int), + Item filters in min gold json array; 2-plot, 3-armor, 4-belts, 5-boots, + 6-cloaks, 7-gems, 8-gloves, 9-headgear, 10-jewelry, 11-misc, 12-potions, + 13-scrolls, 14-shields, 15-wands, 16-weapons, 17-arrow, 18-bolt, 19-bullet. + plugins (jsonarray) - 0+ (string). * Only used in the "pc" data. + location (jsonobject) - geometry (json), used in widgets for pc and associates. +*/////////////////////////////////////////////////////////////////////////////// +const string AI_TABLE = "PEPS_TABLE"; +const string AI_CAMPAIGN_DATABASE = "peps_database"; +const string AI_DM_TABLE = "DM_TABLE"; +#include "0i_constants" +#include "0i_messages" +// Sets PEPS RULES from the database to the module. +// Creates default rules if they do not exist. +void ai_SetAIRules(); +// Returns TRUE if oCreature is controlled by a player. +int ai_GetIsCharacter(object oCreature); +// Returns TRUE if oCreature is controlled by a dungeon master. +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 percentage of hit points oCreature has left. +int ai_GetPercHPLoss(object oCreature); +// Returns a rolled result from sDice string. +// Example: "1d6" will be 1-6 or "3d6" will be 3-18 or 1d6+5 will be 6-11. +int ai_RollDiceString(string sDice); +// Returns the int number of a encoded 0x00000000 hex number from a string. +int ai_HexStringToInt(string sString); +// Returns cosine of the angle between oObject1 and oObject2 +float ai_GetCosAngleBetween(object oObject1, object oObject2); +// Returns a string from sString with only characters in sLegal. +// Used to remove illegal characters for databases. +string ai_RemoveIllegalCharacters(string sString, string sLegal = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); +// Returns the total levels of oCreature. +int ai_GetCharacterLevels(object oCreature); +// Returns a string where sFind is replaced in any occurrence of sSource with sReplace. +string ai_StringReplaceText(string sSource, string sFind, string sReplace); +// Returns a string of characters between the nIndex of predefined markers of +// sSeperator in sText. +// nIndex is the number of the data we are searching for in the array. +// A 0 nIndex is the first item in the text array. +// sSeperator is the character that seperates the array(Usefull for Multiple arrays). +string ai_GetStringArray(string sText, int nIndex, string sSeperator = ":"); +// Returns a string of characters between the nIndex of predefined markers of +// sSeperator in sText where sField has been set. +// sText is the text holding the array. +// nIndex is the array number in the data we are searching for. +// A 0 nIndex is the first item in the text array. +// sField is the field of characters to replace that index. +// sSeperator is the character that seperates the array(Usefull for Multiple arrays). +string ai_SetStringArray(string sText, int nIndex, string sField, string sSeperator = ":"); +// Returns the number of magical properties oItem has. +int ai_GetNumberOfProperties(object oItem); +// Checks if the campaign database table has been created and initialized. +void ai_CheckCampaignDataAndInitialize(); +// Checks if the dm database table and data has been created and initialized of oDM. +void ai_CheckDMDataAndInitialize(object oDM); +// Sets json to a campaign database. +void ai_SetCampaignDbJson(string sDataField, json jData, string sName = "PEPS_DATA", string sTable = AI_TABLE); +// Gets json from a campaign database. +json ai_GetCampaignDbJson(string sDataField, string sName = "PEPS_DATA", string sTable = AI_TABLE); +// Checks if oMaster has the Table created for Associate data. +// If no table found then the table is created and then initialized. +void ai_CheckAssociateDataAndInitialize(object oPlayer, string sAssociateType); +// Returns the associatetype int string format for oAssociate. +// They are pc, familar, companion, summons, henchman is the henchmans tag +string ai_GetAssociateType(object oPlayer, object oAssociate); +// Sets nData to sDataField for sAssociateType that is on oPlayer. +// sDataField can be modes, magicmodes, lootmodes, widgetbuttons, aibuttons, magic, +// healoutcombat, healincombat, mingold*. +void ai_SetAssociateDbInt(object oPlayer, string sAssociateType, string sDataField, int nData, string sTable = AI_TABLE); +// Returns nData from sDataField for sAssociateType that is on oPlayer. +// sDataField can be modes, magicmodes, lootmodes, widgetbuttons, aibuttons, magic, +// healoutcombat, healincombat, mingold*. +int ai_GetAssociateDbInt(object oPlayer, string sAssociateType, string sDataField, string sTable = AI_TABLE); +// Sets fData to sDataField for sAssociateType that is on oPlayer. +// sDataField can be lootrange, lockrange, traprange. +void ai_SetAssociateDbFloat(object oPlayer, string sAssociatetype, string sDataField, float fData, string sTable = AI_TABLE); +// Returns fData from sDataField for sAssociateType that is on oPlayer. +// sDataField can be lootrange, lockrange, traprange. +float ai_GetAssociateDbFloat(object oPlayer, string sAssociateType, string sDataField, string sTable = AI_TABLE); +// sDataField should be one of the data fields for that table. +// jData is the json data to be saved. +void ai_SetAssociateDbJson(object oPlayer, string sAssociateType, string sDataField, json jData, string sTable = AI_TABLE); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +json ai_GetAssociateDbJson(object oPlayer, string sAssociateType, string sDataField, string sTable = AI_TABLE); +// Saves Associate AIModes and MagicModes to the database. +void aiSaveAssociateModesToDb(object oPlayer, object oAssociate); +// Checks Associate local data and if none is found will initialize or load the +// correct data for oAssociate. +void ai_CheckAssociateData(object oPlayer, object oAssociate, string sAssociateType, int bLoad = FALSE); +// Checks DM's local data and if none is found will initizlize or load the +// correct data for oPlayer. +void ai_CheckDMData(object oPlayer); +// Adds to jPlugins functions after checking if the plugin can be installed. +json ai_Plugin_Add(object oPC, json jPlugins, string sPluginScript); +// Updates the players Plugin list and saves to the database. +json ai_UpdatePluginsForPC(object oPC); +// Updates the DM's Plugin list and saves to the database. +json ai_UpdatePluginsForDM (object oPC); +// Runs all plugins that are loaded into the database. +void ai_StartupPlugins(object oPC); +void ai_SetAIRules() +{ + object oModule = GetModule(); + ai_CheckCampaignDataAndInitialize(); + json jRules = ai_GetCampaignDbJson("rules"); + if(JsonGetType(JsonObjectGet(jRules, AI_RULE_MORAL_CHECKS)) == JSON_TYPE_NULL) + { + // Variable name set to a creatures full name to set debugging on. + 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 cast summons spells when prebuffing. + SetLocalInt(oModule, AI_RULE_PRESUMMON, AI_PRESUMMONS); + jRules = JsonObjectSet(jRules, AI_RULE_PRESUMMON, JsonInt(AI_PRESUMMONS)); + // Allows monsters to use tactical AI scripts. + SetLocalInt(oModule, AI_RULE_AMBUSH, AI_TACTICAL); + jRules = JsonObjectSet(jRules, AI_RULE_AMBUSH, JsonInt(AI_TACTICAL)); + // Enemies may summon familiars and Animal companions and will be randomized. + SetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS, AI_SUMMON_COMPANIONS); + jRules = JsonObjectSet(jRules, AI_RULE_SUMMON_COMPANIONS, JsonInt(AI_SUMMON_COMPANIONS)); + // Allow the AI to move during combat base on the situation and action taking. + SetLocalInt(oModule, AI_RULE_ADVANCED_MOVEMENT, AI_ADVANCED_MOVEMENT); + jRules = JsonObjectSet(jRules, AI_RULE_ADVANCED_MOVEMENT, JsonInt(AI_ADVANCED_MOVEMENT)); + // Follow Item Level Restrictions for monsters/associates. + SetLocalInt(oModule, AI_RULE_ILR, AI_ITEM_LEVEL_RESTRICTIONS); + jRules = JsonObjectSet(jRules, AI_RULE_ILR, JsonInt(AI_ITEM_LEVEL_RESTRICTIONS)); + // Allow the AI to use Use Magic Device. + SetLocalInt(oModule, AI_RULE_ALLOW_UMD, AI_USE_MAGIC_DEVICE); + jRules = JsonObjectSet(jRules, AI_RULE_ALLOW_UMD, JsonInt(AI_USE_MAGIC_DEVICE)); + // Allow the AI to use healing kits. + SetLocalInt(oModule, AI_RULE_HEALERSKITS, AI_HEALING_KITS); + jRules = JsonObjectSet(jRules, AI_RULE_HEALERSKITS, JsonInt(AI_HEALING_KITS)); + // Associates are permanent and don't get removed when the master dies. + SetLocalInt(oModule, AI_RULE_PERM_ASSOC, AI_COMPANIONS_PERMANENT); + jRules = JsonObjectSet(jRules, AI_RULE_PERM_ASSOC, JsonInt(AI_COMPANIONS_PERMANENT)); + // Monster AI's chance to attack the weakest target instead of the nearest. + SetLocalInt(oModule, AI_RULE_AI_DIFFICULTY, AI_TARGET_WEAKEST); + jRules = JsonObjectSet(jRules, AI_RULE_AI_DIFFICULTY, JsonInt(AI_TARGET_WEAKEST)); + // Monster AI's distance they can search for the enemy. + SetLocalFloat(oModule, AI_RULE_PERCEPTION_DISTANCE, AI_SEARCH_DISTANCE); + jRules = JsonObjectSet(jRules, AI_RULE_PERCEPTION_DISTANCE, JsonFloat(AI_SEARCH_DISTANCE)); + // Enemy corpses remain on the floor instead of dissappearing. + SetLocalInt(oModule, AI_RULE_CORPSES_STAY, AI_CORPSE_REMAIN); + jRules = JsonObjectSet(jRules, AI_RULE_CORPSES_STAY, JsonInt(AI_CORPSE_REMAIN)); + // Monsters will wander around when not in combat. + SetLocalInt(oModule, AI_RULE_WANDER, AI_WANDER); + jRules = JsonObjectSet(jRules, AI_RULE_WANDER, JsonInt(AI_WANDER)); + // Increase the number of encounter creatures. + SetLocalFloat(oModule, AI_INCREASE_ENC_MONSTERS, 0.0); + jRules = JsonObjectSet(jRules, AI_INCREASE_ENC_MONSTERS, JsonFloat(0.0)); + // Increase all monsters hitpoints by this percentage. + SetLocalInt(oModule, AI_INCREASE_MONSTERS_HP, 0); + jRules = JsonObjectSet(jRules, AI_INCREASE_MONSTERS_HP, JsonInt(0)); + // Monster's perception distance. + SetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE, AI_MONSTER_PERCEPTION); + jRules = JsonObjectSet(jRules, AI_RULE_MON_PERC_DISTANCE, JsonInt(AI_MONSTER_PERCEPTION)); + // Variable name set to hold the maximum number of henchman the player wants. + int nMaxHenchmen = GetMaxHenchmen(); + SetLocalInt(oModule, AI_RULE_MAX_HENCHMAN, nMaxHenchmen); + jRules = JsonObjectSet(jRules, AI_RULE_MAX_HENCHMAN, JsonInt(nMaxHenchmen)); + // Monster AI's distance they can wander away from their spawn point. + SetLocalFloat(oModule, AI_RULE_WANDER_DISTANCE, AI_WANDER_DISTANCE); + jRules = JsonObjectSet(jRules, AI_RULE_WANDER_DISTANCE, JsonFloat(AI_WANDER_DISTANCE)); + // Monsters will open doors when wandering around and not in combat. + SetLocalInt(oModule, AI_RULE_OPEN_DOORS, AI_WANDER); + jRules = JsonObjectSet(jRules, AI_RULE_OPEN_DOORS, JsonInt(AI_OPEN_DOORS)); + // If the modules default XP has not been set then we do it here. + int nDefaultXP = GetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE); + if(nDefaultXP == 0) + { + int nValue = GetModuleXPScale(); + if(nValue != 0) SetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE, nValue); + } + // Variable name set to allow the game to regulate experience based on party size. + SetLocalInt(oModule, AI_RULE_PARTY_SCALE, AI_PARTY_SCALE); + jRules = JsonObjectSet(jRules, AI_RULE_PARTY_SCALE, JsonInt(AI_PARTY_SCALE)); + SetLocalJson(oModule, AI_RULE_RESTRICTED_SPELLS, JsonArray()); + jRules = JsonObjectSet(jRules, AI_RULE_RESTRICTED_SPELLS, JsonArray()); + // Variable name set to allow access to widget buttons for the players. + SetLocalInt(oModule, sDMWidgetAccessVarname, AI_DM_WIDGET_ACCESS_BUTTONS); + jRules = JsonObjectSet(jRules, sDMWidgetAccessVarname, JsonInt(AI_DM_WIDGET_ACCESS_BUTTONS)); + // Variable name set to allow access to widget buttons for the players. + SetLocalInt(oModule, sDMAIAccessVarname, AI_DM_AI_ACCESS_BUTTONS); + jRules = JsonObjectSet(jRules, sDMAIAccessVarname, JsonInt(AI_DM_AI_ACCESS_BUTTONS)); + ai_SetCampaignDbJson("rules", jRules); + } + else + { + // Variable name set to a creatures full name to set debugging on. + string sValue = JsonGetString(JsonObjectGet(jRules, AI_RULE_DEBUG_CREATURE)); + SetLocalString(oModule, AI_RULE_DEBUG_CREATURE, sValue); + // Moral checks on or off. + int bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_MORAL_CHECKS)); + SetLocalInt(oModule, AI_RULE_MORAL_CHECKS, bValue); + // Allows monsters to prebuff before combat starts. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_BUFF_MONSTERS)); + SetLocalInt(oModule, AI_RULE_BUFF_MONSTERS, bValue); + // Allows monsters cast summons spells when prebuffing. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_PRESUMMON)); + SetLocalInt(oModule, AI_RULE_PRESUMMON, bValue); + // Allows monsters to use ambush AI scripts. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_AMBUSH)); + SetLocalInt(oModule, AI_RULE_AMBUSH, bValue); + // Enemies may summon familiars and Animal companions and will be randomized. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_SUMMON_COMPANIONS)); + SetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS, bValue); + // Allow the AI to move during combat base on the situation and action taking. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_ADVANCED_MOVEMENT)); + SetLocalInt(oModule, AI_RULE_ADVANCED_MOVEMENT, bValue); + // Follow Item Level Restrictions for monsters/associates. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_ILR)); + SetLocalInt(oModule, AI_RULE_ILR, bValue); + // Allow the AI to use Use Magic Device. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_ALLOW_UMD)); + SetLocalInt(oModule, AI_RULE_ALLOW_UMD, bValue); + // Allow the AI to use healing kits. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_HEALERSKITS)); + SetLocalInt(oModule, AI_RULE_HEALERSKITS, bValue); + // Associates are permanent and don't get removed when the owner dies. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_PERM_ASSOC)); + SetLocalInt(oModule, AI_RULE_PERM_ASSOC, bValue); + // Monster AI's chance to attack the weakest target instead of the nearest. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_AI_DIFFICULTY)); + SetLocalInt(oModule, AI_RULE_AI_DIFFICULTY, bValue); + // Monster AI's perception distance from player. + float fValue = JsonGetFloat(JsonObjectGet(jRules, AI_RULE_PERCEPTION_DISTANCE)); + SetLocalFloat(oModule, AI_RULE_PERCEPTION_DISTANCE, fValue); + // Enemy corpses remain on the floor instead of dissappearing. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_CORPSES_STAY)); + SetLocalInt(oModule, AI_RULE_CORPSES_STAY, bValue); + // Monsters will wander around when not in combat. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_WANDER)); + SetLocalInt(oModule, AI_RULE_WANDER, bValue); + // Increase the number of encounter creatures. + fValue = JsonGetFloat(JsonObjectGet(jRules, AI_INCREASE_ENC_MONSTERS)); + SetLocalFloat(oModule, AI_INCREASE_ENC_MONSTERS, fValue); + // Increase all monsters hitpoints by this percentage. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_INCREASE_MONSTERS_HP)); + SetLocalInt(oModule, AI_INCREASE_MONSTERS_HP, bValue); + // Monster's perception distance. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_MON_PERC_DISTANCE)); + if(bValue < 8 || bValue > 11) bValue = 11; + SetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE, bValue); + // Variable name set to hold the maximum number of henchman the player wants. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_MAX_HENCHMAN)); + if(bValue == 0) bValue = GetMaxHenchmen(); + else SetMaxHenchmen(bValue); + SetLocalInt(oModule, AI_RULE_MAX_HENCHMAN, bValue); + // Monster AI's wander distance from their spawn point. + fValue = JsonGetFloat(JsonObjectGet(jRules, AI_RULE_WANDER_DISTANCE)); + SetLocalFloat(oModule, AI_RULE_WANDER_DISTANCE, fValue); + // Monsters will open doors while wandering around and not in combat. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_OPEN_DOORS)); + SetLocalInt(oModule, AI_RULE_OPEN_DOORS, bValue); + // If the modules default XP has not been set then we do it here. + int nDefaultXP = GetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE); + if(nDefaultXP == 0) + { + bValue = GetModuleXPScale(); + if(bValue != 0) SetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE, bValue); + } + // Variable name set to allow the game to regulate experience based on party size. + bValue = JsonGetInt(JsonObjectGet(jRules, AI_RULE_PARTY_SCALE)); + if(bValue) + { + int nBasePartyXP = GetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP); + if(nBasePartyXP == 0) + { + nDefaultXP = GetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE); + SetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP, nDefaultXP); + } + } + SetLocalInt(oModule, AI_RULE_PARTY_SCALE, bValue); + json jRSpells = JsonObjectGet(jRules, AI_RULE_RESTRICTED_SPELLS); + if(JsonGetType(jRSpells) == JSON_TYPE_NULL) + { + jRSpells = JsonArray(); + jRules = JsonObjectSet(jRules, AI_RULE_RESTRICTED_SPELLS, jRSpells); + ai_SetCampaignDbJson("rules", jRules); + } + SetLocalJson(oModule, AI_RULE_RESTRICTED_SPELLS, jRSpells); + // Variable name set to allow access to widget buttons for the players. + bValue = JsonGetInt(JsonObjectGet(jRules, sDMWidgetAccessVarname)); + SetLocalInt(oModule, sDMWidgetAccessVarname, bValue); + // Variable name set to allow access to widget buttons for the players. + bValue = JsonGetInt(JsonObjectGet(jRules, sDMAIAccessVarname)); + SetLocalInt(oModule, sDMAIAccessVarname, bValue); + } +} +int ai_GetIsCharacter(object oCreature) +{ + return (GetIsPC(oCreature) && !GetIsDM(oCreature) && !GetIsDMPossessed(oCreature)); +} +int ai_GetIsDungeonMaster(object oCreature) +{ + return (GetIsDM(oCreature) || GetIsDMPossessed(oCreature)); +} +object ai_GetPlayerMaster(object oAssociate) +{ + if(ai_GetIsCharacter(oAssociate)) return oAssociate; + object oMaster = GetMaster(oAssociate); + if(ai_GetIsCharacter(oMaster)) return oMaster; + return OBJECT_INVALID; +} +int ai_GetPercHPLoss(object oCreature) +{ + int nHP = GetCurrentHitPoints(oCreature); + if(nHP < 1) return 0; + return(nHP * 100) / GetMaxHitPoints(oCreature); +} +int ai_RollDiceString(string sDice) +{ + int nNegativePos, nBonus = 0; + string sRight = GetStringRight(sDice, GetStringLength(sDice) - FindSubString(sDice, "d") - 1); + int nPlusPos = FindSubString(sRight, "+"); + if(nPlusPos != -1) + { + nBonus = StringToInt(GetStringRight(sRight, GetStringLength(sRight) - nPlusPos - 1)); + sRight = GetStringLeft(sRight, nPlusPos); + } + else + { + nNegativePos = FindSubString(sRight, "-"); + if(nNegativePos != -1) + { + nBonus = StringToInt(GetStringRight(sRight, GetStringLength(sRight) - nNegativePos - 1)); + sRight = GetStringLeft(sRight, nNegativePos); + nBonus = nBonus * -1; + } + } + int nDie = StringToInt(sRight); + int nNumOfDie = StringToInt(GetStringLeft(sDice, FindSubString(sDice, "d"))); + int nResult; + while(nNumOfDie > 0) + { + nResult += Random(nDie) + 1; + nNumOfDie --; + } + return nResult + nBonus; +} +int ai_HexStringToInt(string sString) +{ + sString = GetStringLowerCase(sString); + int nInt = 0; + int nLength = GetStringLength(sString); + int i; + for(i = nLength - 1; i >= 0; i--) + { + int n = FindSubString("0123456789abcdef", GetSubString(sString, i, 1)); + if(n == -1) return nInt; + nInt |= n << ((nLength - i - 1) * 4); + } + return nInt; +} +float ai_GetCosAngleBetween(object oObject1, object oObject2) +{ + vector v1 = GetPositionFromLocation(GetLocation(oObject1)); + vector v2 = GetPositionFromLocation(GetLocation(oObject2)); + vector v3 = GetPositionFromLocation(GetLocation(OBJECT_SELF)); + + v1.x -= v3.x; v1.y -= v3.y; v1.z -= v3.z; + v2.x -= v3.x; v2.y -= v3.y; v2.z -= v3.z; + + float dotproduct = v1.x*v2.x+v1.y*v2.y+v1.z*v2.z; + + return dotproduct/(VectorMagnitude(v1)*VectorMagnitude(v2)); +} +string ai_RemoveIllegalCharacters(string sString, string sLegal = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") +{ + string sOut, sValue; + sString = ai_StripColorCodes(sString); + int nLength = GetStringLength(sString); + int Cnt; + for(Cnt = 0; Cnt != nLength; ++Cnt) + { + sValue = GetSubString(sString, Cnt, 1); + if(TestStringAgainstPattern("**" + sValue + "**", sLegal)) + sOut += sValue; + } + return sOut; +} +int ai_GetCharacterLevels(object oCreature) +{ + int nLevels, nPosition = 1; + while(nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + nLevels += GetLevelByPosition(nPosition, oCreature); + nPosition++; + } + return nLevels; +} +string ai_StringReplaceText(string sSource, string sFind, string sReplace) +{ + int nFindLength = GetStringLength(sFind); + int nPosition = 0; + string sReturnValue = ""; + // Locate all occurences of sFind. + int nFound = FindSubString(sSource, sFind); + while(nFound >= 0 ) + { + // Build the return string, replacing this occurence of sFind with sReplace. + sReturnValue += GetSubString(sSource, nPosition, nFound - nPosition) + sReplace; + nPosition = nFound + nFindLength; + nFound = FindSubString(sSource, sFind, nPosition); + } + // Tack on the end of sSource and return. + return sReturnValue + GetStringRight(sSource, GetStringLength(sSource) - nPosition); +} +string ai_GetStringArray(string sArray, int nIndex, string sSeperator = ":") +{ + int nCnt = 0, nMark = 0, nStringLength = GetStringLength(sArray); + string sCharacter; + // Search the string. + while(nCnt < nStringLength) + { + sCharacter = GetSubString(sArray, nCnt, 1); + // Look for the mark. + if(sCharacter == sSeperator) + { + // If we have not found it then lets see if this mark is the one. + if(nMark < 1) + { + // If we are down to 0 in the index then we have found the mark. + if(nIndex > 0) nIndex --; + // Mark the start of the string we need. + else nMark = nCnt + 1; + } + else + { + // We have the first mark so the next mark will mean we have the string we need. + // Now pull it and return. + sArray = GetSubString(sArray, nMark, nCnt - nMark); + return sArray; + } + } + nCnt ++; + } + // If we hit the end without finding it then return "" as an error. + return ""; +} +string ai_SetStringArray(string sArray, int nIndex, string sField, string sSeperator = ":") +{ + int nCnt = 1, nMark = 1, nStringLength = GetStringLength(sArray); + int nIndexCounter = 0; + string sCharacter, sNewArray = sSeperator, sText; + // Check to make sure this is not a new array. + // If it is new then set it with 1 slot. + if(nStringLength < 2) + { + sArray = sSeperator + " " + sSeperator; + nStringLength = 3; + } + // Search the string. + while(nCnt <= nStringLength) + { + sCharacter = GetSubString(sArray, nCnt, 1); + // Look for the mark. + if(sCharacter == sSeperator) + { + // First check to see if this is the index we are replacing. + if(nIndex == nIndexCounter) sText = sField; + else + { + // Get the original text for this field. + sText = GetSubString(sArray, nMark, nCnt - nMark); + } + // Add the field to the new index. + sNewArray = sNewArray + sText + sSeperator; + // Now set the marker to the new starting point. + nMark = nCnt + 1; + // Increase the index counter as well. + nIndexCounter ++; + } + nCnt ++; + } + // if we are at the end of the array and still have not set the data + // then add blank data until we get to the correct index. + while(nIndexCounter <= nIndex) + { + // If they match add the field. + if(nIndexCounter == nIndex) sNewArray = sNewArray + sField + sSeperator; + // Otherwise just add a blank field. + else sNewArray = sNewArray + " " + sSeperator; + nIndexCounter ++; + } + // When done return the new array. + return sNewArray; +} +int ai_GetNumberOfProperties(object oItem) +{ + int nNumOfProperties = 0, nPropertyType, nPropertySubType; + // Get first property + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + // Ignore double type properties such as bane. + nPropertyType = GetItemPropertyType(ipProperty); + switch(nPropertyType) + { + // Skip these properties as they don't count. + case 8 : break; // EnhanceAlignmentGroup + case 44 : break; // Light + case 62 : break; // UseLimitationAlignmentGroup + case 63 : break; // UseLimitationClass + case 64 : break; // UseLimitationRacial + case 65 : break; // UseLimitationSpecificAlignment + case 66 : break; // UseLimitationTerrain + case 86 : break; // Quality + case 150 : break; // UseLimitationGender + case 15 : + { + nPropertySubType = GetItemPropertySubType(ipProperty); + if(nPropertySubType == IP_CONST_CASTSPELL_UNIQUE_POWER_SELF_ONLY) break; + if(nPropertySubType == IP_CONST_CASTSPELL_UNIQUE_POWER) break; + } + default : nNumOfProperties ++; + } + // Get the next property + ipProperty = GetNextItemProperty(oItem); + } + // Reduce the number of properties by one on whips. + if(GetBaseItemType(oItem) == BASE_ITEM_WHIP) nNumOfProperties --; + return nNumOfProperties; +} +void ai_CreateCampaignDataTable() +{ + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, + "CREATE TABLE IF NOT EXISTS " + AI_TABLE + "(" + + "name TEXT, " + + "plugins TEXT, " + + "rules TEXT, " + + "PRIMARY KEY(name));"); + SqlStep(sql); + //if(AI_DEBUG) ai_Debug("0i_main", "343", We are creating a campaign table [" + + // AI_TABLE + "] in the database."); +} +void ai_CheckCampaignDataTableAndCreateTable() +{ + string sQuery = "SELECT name FROM sqlite_master WHERE type ='table' " + + "AND name =@table;"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindString(sql, "@table", AI_TABLE); + if(!SqlStep(sql)) ai_CreateCampaignDataTable(); +} +void ai_InitializeCampaignData() +{ + string sQuery = "INSERT INTO " + AI_TABLE + "(name, plugins, rules) " + + "VALUES(@name, @plugins, @rules);"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindString(sql, "@name", "PEPS_DATA"); + SqlBindJson(sql, "@plugins", JsonArray()); + SqlBindJson(sql, "@rules", JsonObject()); + SqlStep(sql); +} +void ai_CheckCampaignDataAndInitialize() +{ + ai_CheckCampaignDataTableAndCreateTable(); + string sQuery = "SELECT name FROM " + AI_TABLE + " WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindString(sql, "@name", "PEPS_DATA"); + if(!SqlStep(sql)) ai_InitializeCampaignData(); +} +void ai_CreateDMDataTable() +{ + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, + "CREATE TABLE IF NOT EXISTS " + AI_DM_TABLE + "(" + + "name TEXT, " + + "buttons TEXT, " + + "plugins TEXT, " + + "locations TEXT, " + + "options TEXT, " + + "saveslots TEXT, " + + "PRIMARY KEY(name));"); + SqlStep(sql); +} +void ai_CheckDMDataTableAndCreateTable() +{ + string sQuery = "SELECT name FROM sqlite_master WHERE type ='table' " + + "AND name =@table;"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindString(sql, "@table", AI_DM_TABLE); + if(!SqlStep(sql)) ai_CreateDMDataTable(); +} +void ai_InitializeDMData(string sName) +{ + string sQuery = "INSERT INTO " + AI_DM_TABLE + "(name, buttons, plugins, " + + "locations, options, saveslots) " + + "VALUES(@name, @buttons, @plugins, @locations, @options, @saveslots);"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindJson(sql, "@buttons", JsonArray()); + SqlBindJson(sql, "@plugins", JsonArray()); + SqlBindJson(sql, "@locations", JsonObject()); + SqlBindJson(sql, "@options", JsonObject()); + SqlBindJson(sql, "@saveslots", JsonObject()); + SqlStep(sql); +} +void ai_CheckDMDataAndInitialize(object oDM) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oDM))); + string sQuery = "SELECT name FROM " + AI_DM_TABLE + " WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindString(sql, "@name", sName); + if(!SqlStep(sql)) + { + ai_CheckDMDataTableAndCreateTable(); + ai_InitializeDMData(sName); + } +} +void ai_SetCampaignDbJson(string sDataField, json jData, string sName = "PEPS_DATA", string sTable = AI_TABLE) +{ + string sQuery = "UPDATE " + sTable + " SET " + sDataField + + " = @data WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindJson(sql, "@data", jData); + SqlBindString(sql, "@name", sName); + SqlStep(sql); +} +json ai_GetCampaignDbJson(string sDataField, string sName = "PEPS_DATA", string sTable = AI_TABLE) +{ + string sQuery = "SELECT " + sDataField + " FROM " + sTable + " WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryCampaign(AI_CAMPAIGN_DATABASE, sQuery); + SqlBindString(sql, "@name", sName); + json jReturn; + if(SqlStep(sql)) return SqlGetJson (sql, 0); + else return JsonArray(); + return jReturn; +} +void ai_CreateAssociateDataTable(object oPlayer) +{ + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "CREATE TABLE IF NOT EXISTS " + AI_TABLE + "(" + + "name TEXT, " + + "modes TEXT, " + + "buttons TEXT, " + + "aidata TEXT, " + + "lootfilters TEXT, " + + "plugins TEXT, " + + "locations TEXT, " + + "PRIMARY KEY(name));"); + SqlStep(sql); + //ai_Debug("0i_main", "665", GetName(oPlayer) + " is creating a table [" + + // AI_TABLE + "] in the database."); +} +void ai_CheckDataTableAndCreateTable(object oPlayer) +{ + string sQuery = "SELECT name FROM sqlite_master WHERE type ='table' " + + "AND name =@table;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@table", AI_TABLE); + if(!SqlStep(sql)) ai_CreateAssociateDataTable (oPlayer); + //else SendMessageToPC(oPlayer, "0i_main, 675, " + GetName(oPlayer) + " has a database with table [" + AI_TABLE + "]."); +} +void ai_InitializeAssociateData(object oPlayer, string sAssociateType) +{ + string sQuery = "INSERT INTO " + AI_TABLE + "(name, modes, buttons, " + + "aidata, lootfilters, plugins, locations) " + + "VALUES(@name, @modes, @buttons, @aidata, @lootfilters, @plugins, @locations);"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sAssociateType); + SqlBindJson(sql, "@modes", JsonArray()); + SqlBindJson(sql, "@buttons", JsonArray()); + SqlBindJson(sql, "@aidata", JsonArray()); + SqlBindJson(sql, "@lootfilters", JsonArray()); + SqlBindJson(sql, "@plugins", JsonArray()); + SqlBindJson(sql, "@locations", JsonObject()); + //SendMessageToPC(oPlayer, "0i_main, 690, " + GetName(oPlayer) + " is initializing associate " + + // sAssociateType + " data for table [" + AI_TABLE + "]."); + SqlStep(sql); +} +void ai_CheckAssociateDataAndInitialize(object oPlayer, string sAssociateType) +{ + ai_CheckDataTableAndCreateTable(oPlayer); + string sQuery = "SELECT name FROM " + AI_TABLE + " WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryObject (oPlayer, sQuery); + SqlBindString(sql, "@name", sAssociateType); + if(!SqlStep(sql)) ai_InitializeAssociateData(oPlayer, sAssociateType); + //else SendMessageToPC(oPlayer, "0i_main, 701, sAssociateType: " + sAssociateType + + // " returns: " + SqlGetString(sql, 0)); +} +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_HENCHMAN) + { + sAITag = GetTag(oAssociate); + oCreature = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPlayer, nIndex); + // 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); + } + } + else if(nAssociateType == ASSOCIATE_TYPE_SUMMONED) + { + 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); + } + return sAITag; +} +void ai_SetAssociateDbInt(object oPlayer, string sAssociatetype, string sDataField, int nData, string sTable = AI_TABLE) +{ + string sQuery = "UPDATE " + sTable + " SET " + sDataField + + " = @data WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sAssociatetype); + SqlBindInt(sql, "@data", nData); + //ai_Debug("0i_main", "368", "SETTING DATA: " + GetName(oPlayer) + " sAssociatetype: " + + // sAssociatetype + " sDataField: " + sDataField + " nData: " + IntToString(nData)); + SqlStep(sql); +} +int ai_GetAssociateDbInt(object oPlayer, string sAssociatetype, string sDataField, string sTable = AI_TABLE) +{ + string sQuery = "SELECT " + sDataField + " FROM " + sTable + " WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sAssociatetype); + //ai_Debug("0i_main", "377", "GETTING DATA: " + GetName(oPlayer) + " sAssociatetype: " + + // sAssociatetype + " sDataField: " + sDataField); + if(SqlStep(sql)) return SqlGetInt(sql, 0); + else return 0; +} +void ai_SetAssociateDbFloat(object oPlayer, string sAssociatetype, string sDataField, float fData, string sTable = AI_TABLE) +{ + string sQuery = "UPDATE " + sTable + " SET " + sDataField + + " = @data WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sAssociatetype); + SqlBindFloat(sql, "@data", fData); + //ai_Debug("0i_main", "368", "SETTING DATA: " + GetName(oPlayer) + " sAssociatetype: " + + // sAssociatetype + " sDataField: " + sDataField + " fData: " + FloatToString(fData, 0, 0)); + SqlStep(sql); +} +float ai_GetAssociateDbFloat(object oPlayer, string sAssociatetype, string sDataField, string sTable = AI_TABLE) +{ + string sQuery = "SELECT " + sDataField + " FROM " + sTable + " WHERE name = @name;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sAssociatetype); + //ai_Debug("0i_main", "377", "GETTING DATA: " + GetName(oPlayer) + " sAssociatetype: " + + // sAssociatetype + " sDataField: " + sDataField); + if(SqlStep(sql)) return SqlGetFloat(sql, 0); + else return 0.0; +} +void ai_SetAssociateDbJson(object oPlayer, string sAssociateType, string sDataField, json jData, string sTable = AI_TABLE) +{ + //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); + SqlBindJson(sql, "@data", jData); + SqlBindString(sql, "@name", sAssociateType); + SqlStep(sql); +} +json ai_GetAssociateDbJson(object oPlayer, string sAssociateType, string sDataField, string sTable = AI_TABLE) +{ + //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); + if(SqlStep(sql)) + { + json jReturn = SqlGetJson(sql, 0); + //SendMessageToPC(oPlayer, "0i_main, 646 jReturn: " + JsonDump(jReturn, 1)); + if(JsonGetType(jReturn) == JSON_TYPE_NULL) return JsonArray(); + return jReturn; + } + else return JsonNull(); +} +void aiSaveAssociateModesToDb(object oPlayer, object oAssociate) +{ + string sAssociateType = ai_GetAssociateType(oPlayer, oAssociate); + json jModes = ai_GetAssociateDbJson(oPlayer, sAssociateType, "modes"); + int nAIMode = GetLocalInt(oAssociate, sAIModeVarname); + jModes = JsonArraySet(jModes, 0, JsonInt(nAIMode)); + int nMagicMode = GetLocalInt(oAssociate, sMagicModeVarname); + jModes = JsonArraySet(jModes, 1, JsonInt(nMagicMode)); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "modes", jModes); +} +void ai_SetupModes(object oPlayer, object oAssociate, string sAssociateType) +{ + json jModes = JsonArray(); + jModes = JsonArrayInsert(jModes, JsonInt(0)); // AI Modes. + // Set magic modes to use Normal magic, Bit 256. + jModes = JsonArrayInsert(jModes, JsonInt(256)); // Magic Modes. + SetLocalInt(oAssociate, sMagicModeVarname, 256); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "modes", jModes, AI_TABLE); +} +void ai_SetupButtons(object oPlayer, object oAssociate, string sAssociateType) +{ + json jButtons = JsonArray(); + jButtons = JsonArrayInsert(jButtons, JsonInt(0)); // Command buttons. + jButtons = JsonArrayInsert(jButtons, JsonInt(0)); // AI buttons. + jButtons = JsonArrayInsert(jButtons, JsonInt(0)); // AI buttons 2. + ai_SetAssociateDbJson(oPlayer, sAssociateType, "buttons", jButtons, AI_TABLE); +} +void ai_SetupAIData(object oPlayer, object oAssociate, string sAssociateType) +{ + json jAIData = JsonArray(); + jAIData = JsonArrayInsert(jAIData, JsonInt(0)); // 0 - Difficulty adjustment. + jAIData = JsonArrayInsert(jAIData, JsonInt(70)); // 1 - Heal out of combat. + SetLocalInt(oAssociate, AI_HEAL_OUT_OF_COMBAT_LIMIT, 70); + jAIData = JsonArrayInsert(jAIData, JsonInt(50)); // 2 - Heal in combat. + SetLocalInt(oAssociate, AI_HEAL_IN_COMBAT_LIMIT, 50); + jAIData = JsonArrayInsert(jAIData, JsonFloat(20.0)); // 3 - Loot check range. + SetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE, 20.0); + jAIData = JsonArrayInsert(jAIData, JsonFloat(20.0)); // 4 - Lock check range. + SetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE, 20.0); + jAIData = JsonArrayInsert(jAIData, JsonFloat(20.0)); // 5 - Trap check range. + SetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE, 20.0); + jAIData = JsonArrayInsert(jAIData, JsonFloat(3.0)); // 6 - Associate Distance. + SetLocalFloat(oAssociate, AI_FOLLOW_RANGE, 3.0); + // This can be replaced as it is not used in the database. + // 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); + 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. + ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData, AI_TABLE); +} +void ai_SetupLootFilters(object oPlayer, object oAssociate, string sAssociateType) +{ + json jLootFilters = JsonArray(); + // Maximum weight to pickup an item. + jLootFilters = JsonArrayInsert(jLootFilters, JsonInt(200)); + SetLocalInt(oAssociate, AI_MAX_LOOT_WEIGHT, 200); + // Bitwise int for checkbox pickup filter. + jLootFilters = JsonArrayInsert(jLootFilters, JsonInt(AI_LOOT_ALL_ON)); + SetLocalInt(oAssociate, sLootFilterVarname, AI_LOOT_ALL_ON); + // Minimum gold value to pickup. + int nIndex; + for(nIndex = 2; nIndex < 20; nIndex++) + { + jLootFilters = JsonArrayInsert(jLootFilters, JsonInt(0)); + } + ai_SetAssociateDbJson(oPlayer, sAssociateType, "lootfilters", jLootFilters, AI_TABLE); +} +void ai_SetupLocations(object oPlayer, object oAssociate, string sAssociateType) +{ + json jLocations = JsonObject(); + json jNUI = JsonObject(); + jNUI = JsonObjectSet(jNUI, "x", JsonFloat(-1.0)); + jNUI = JsonObjectSet(jNUI, "y", JsonFloat(-1.0)); + if(ai_GetIsCharacter(oAssociate)) + { + jLocations = JsonObjectSet(jLocations, AI_MAIN_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, AI_PLUGIN_NUI, jNUI); + } + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_COMMAND_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_LOOTFILTER_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_COPY_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_QUICK_WIDGET_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_SPELL_MEMORIZE_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_SPELL_KNOWN_NUI, jNUI); + jNUI = JsonObjectSet(jNUI, "x", JsonFloat(0.0)); + jNUI = JsonObjectSet(jNUI, "y", JsonFloat(0.0)); + jLocations = JsonObjectSet(jLocations, sAssociateType + AI_WIDGET_NUI, jNUI); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "locations", jLocations, AI_TABLE); +} +void ai_SetupAssociateData(object oPlayer, object oAssociate, string sAssociateType) +{ + //ai_Debug("0i_main", "744", GetName(oAssociate) + " is initializing associate data."); + ai_CheckAssociateDataAndInitialize(oPlayer, sAssociateType); + // Default behavior for associates at start. + ai_SetupModes(oPlayer, oAssociate, sAssociateType); + ai_SetupButtons(oPlayer, oAssociate, sAssociateType); + ai_SetupAIData(oPlayer, oAssociate, sAssociateType); + ai_SetupLootFilters(oPlayer, oAssociate, sAssociateType); + // ********** Plugins ************ + // These are pulled straight from the database. + ai_SetupLocations(oPlayer, oAssociate, sAssociateType); +} +void ai_RestoreDatabase(object oPlayer, object oAssociate, string sAssociateType) +{ + // ********** Modes ********** + json jModes = JsonArray(); + // AI Modes (0). + int nValue = GetLocalInt(oAssociate, sAIModeVarname); + jModes = JsonArrayInsert(jModes, JsonInt(nValue)); + // Magic Modes (1). + nValue = GetLocalInt(oAssociate, sMagicModeVarname); + jModes = JsonArrayInsert(jModes, JsonInt(nValue)); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "modes", jModes, AI_TABLE); + // ********** Buttons ********** + json jButtons = JsonArray(); + // Command buttons (0). + nValue = GetLocalInt(oAssociate, sWidgetButtonsVarname); + jButtons = JsonArrayInsert(jButtons, JsonInt(nValue)); + // AI buttons Group 1 (1). + nValue = GetLocalInt(oAssociate, sAIButtonsVarname); + jButtons = JsonArrayInsert(jButtons, JsonInt(nValue)); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "buttons", jButtons, AI_TABLE); + // ********** AI Data ********** + json jAIData = JsonArray(); + nValue = GetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT); + jAIData = JsonArrayInsert(jAIData, JsonInt(nValue)); + nValue = GetLocalInt(oAssociate, AI_HEAL_OUT_OF_COMBAT_LIMIT); + jAIData = JsonArrayInsert(jAIData, JsonInt(nValue)); + nValue = GetLocalInt(oAssociate, AI_HEAL_IN_COMBAT_LIMIT); + jAIData = JsonArrayInsert(jAIData, JsonInt(nValue)); + float fValue = GetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE); + jAIData = JsonArrayInsert(jAIData, JsonFloat(fValue)); + fValue = GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE); + jAIData = JsonArrayInsert(jAIData, JsonFloat(fValue)); + fValue = GetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE); + jAIData = JsonArrayInsert(jAIData, JsonFloat(fValue)); + fValue = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE); + jAIData = JsonArrayInsert(jAIData, JsonFloat(fValue)); + nValue = GetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION); + jAIData = JsonArrayInsert(jAIData, JsonInt(nValue)); + float fRange = 20.0; + if(nValue == 8) fRange = 10.0; + else if(nValue == 10) fRange = 35.0; + SetLocalFloat(oAssociate, AI_ASSOC_PERCEPTION_DISTANCE, fRange); + string sValue = GetLocalString(oAssociate, AI_DEFAULT_SCRIPT); + jAIData = JsonArrayInsert(jAIData, JsonString(sValue)); + fValue = GetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE); + jAIData = JsonArrayInsert(jAIData, JsonFloat(fValue)); + json jValue = GetLocalJson(oPlayer, AI_SPELLS_WIDGET); + if(JsonGetType(jValue) == JSON_TYPE_NULL) + { + jValue = JsonArray(); + jValue = JsonArrayInsert(jValue, JsonInt(1)); // 0 - Class selected. + jValue = JsonArrayInsert(jValue, JsonInt(10)); // 1 - Level selected. + jValue = JsonArrayInsert(jValue, JsonArray()); // Spell list for widget. + SetLocalJson(oPlayer, AI_SPELLS_WIDGET, jValue); + } + jAIData = JsonArrayInsert(jAIData, jValue); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData); + // ********** LootFilters ********** + json jLootFilters = JsonArray(); + nValue = GetLocalInt(oAssociate, AI_MAX_LOOT_WEIGHT); + jLootFilters = JsonArrayInsert(jLootFilters, JsonInt(nValue)); + nValue = GetLocalInt(oAssociate, sLootFilterVarname); + jLootFilters = JsonArrayInsert(jLootFilters, JsonInt(nValue)); + int nIndex; + for(nIndex = 2; nIndex < 20; nIndex++) + { + nValue = GetLocalInt(oAssociate, AI_MIN_GOLD_ + IntToString(nIndex)); + jLootFilters = JsonArrayInsert(jLootFilters, JsonInt(nValue)); + } + ai_SetAssociateDbJson(oPlayer, sAssociateType, "lootfilters", jLootFilters, AI_TABLE); + // ********** Plugins ************ + // These are pulled straight from the database. + // ********** Locations ********** + // These are only in the database. +} +void ai_CheckAssociateData(object oPlayer, object oAssociate, string sAssociateType, int bLoad = FALSE) +{ + //ai_Debug("0i_main", "810", "Checking data for oAssociate: " + GetName(oAssociate)); + // Do quick check to see if they have a variable saved if so then exit. + if(GetLocalFloat(oAssociate, AI_ASSOC_PERCEPTION_DISTANCE) != 0.0) + { + if(!bLoad) return; + // If the database gets destroyed lets drop an error and restore values + // from the locals. + ai_CheckAssociateDataAndInitialize(oPlayer, sAssociateType); + ai_RestoreDatabase(oPlayer, oAssociate, sAssociateType); + return; + } + ai_CheckAssociateDataAndInitialize(oPlayer, sAssociateType); + // ********** Modes ********** + json jModes = ai_GetAssociateDbJson(oPlayer, sAssociateType, "modes"); + if(JsonGetType(JsonArrayGet(jModes, 0)) == JSON_TYPE_NULL) + { + ai_SetupModes(oPlayer, oAssociate, sAssociateType); + } + else + { + SetLocalInt(oAssociate, sAIModeVarname, JsonGetInt(JsonArrayGet(jModes, 0))); + SetLocalInt(oAssociate, sMagicModeVarname, JsonGetInt(JsonArrayGet(jModes, 1))); + } + // ********** Buttons ********** + json jButtons = ai_GetAssociateDbJson(oPlayer, sAssociateType, "buttons"); + if(JsonGetType(JsonArrayGet(jButtons, 0)) == JSON_TYPE_NULL) + { + ai_SetupButtons(oPlayer, oAssociate, sAssociateType); + } + else + { + // ********** Associate Command Buttons ********** + int nWidgetButtons = JsonGetInt(JsonArrayGet(jButtons, 0)); + if(nWidgetButtons) SetLocalInt(oAssociate, sWidgetButtonsVarname, nWidgetButtons); + // ********** Associate AI Buttons ********** + int nAIButtons = JsonGetInt(JsonArrayGet(jButtons, 1)); + if(nAIButtons) SetLocalInt(oAssociate, sAIButtonsVarname, nAIButtons); + } + // ********** AI Data ********** + json jAIData = ai_GetAssociateDbJson(oPlayer, sAssociateType, "aidata"); + if(JsonGetType(JsonArrayGet(jAIData, 0)) == JSON_TYPE_NULL) + { + ai_SetupAIData(oPlayer, oAssociate, sAssociateType); + } + else + { + SetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT, JsonGetInt(JsonArrayGet(jAIData, 0))); + SetLocalInt(oAssociate, AI_HEAL_OUT_OF_COMBAT_LIMIT, JsonGetInt(JsonArrayGet(jAIData, 1))); + SetLocalInt(oAssociate, AI_HEAL_IN_COMBAT_LIMIT, JsonGetInt(JsonArrayGet(jAIData, 2))); + SetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE, JsonGetFloat(JsonArrayGet(jAIData, 3))); + SetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE, JsonGetFloat(JsonArrayGet(jAIData, 4))); + SetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE, JsonGetFloat(JsonArrayGet(jAIData, 5))); + SetLocalFloat(oAssociate, AI_FOLLOW_RANGE, JsonGetFloat(JsonArrayGet(jAIData, 6))); + int nPercRange = JsonGetInt(JsonArrayGet(jAIData, 7)); + if(nPercRange < 8 || nPercRange > 11) nPercRange = 11; + SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION, nPercRange); + float fRange = 20.0; + if(nPercRange == 8) fRange = 10.0; + else if(nPercRange == 10) fRange = 35.0; + SetLocalFloat(oAssociate, AI_ASSOC_PERCEPTION_DISTANCE, fRange); + string sScript = JsonGetString(JsonArrayGet(jAIData, 8)); + if(sScript != "") SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript); + json jDoorRange = JsonArrayGet(jAIData, 9); + if(JsonGetType(jDoorRange) == JSON_TYPE_NULL) + { + jAIData = JsonArrayInsert(jAIData, JsonFloat(20.0)); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData); + SetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE, 20.0); + } + else SetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE, JsonGetFloat(jDoorRange)); + json jSpellsWidget = JsonArrayGet(jAIData, 10); + if(JsonGetType(jSpellsWidget) == JSON_TYPE_NULL) + { + jSpellsWidget = JsonArray(); + jSpellsWidget = JsonArrayInsert(jSpellsWidget, JsonInt(0)); // 0 - Class selected. + jSpellsWidget = JsonArrayInsert(jSpellsWidget, JsonInt(0)); // 1 - Level selected. + jAIData = JsonArrayInsert(jAIData, jSpellsWidget); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "aidata", jAIData); + SetLocalJson(oPlayer, AI_SPELLS_WIDGET, jSpellsWidget); + } + } + // ********** LootFilters ********** + json jLootFilters = ai_GetAssociateDbJson(oPlayer, sAssociateType, "lootfilters"); + if(JsonGetType(JsonArrayGet(jLootFilters, 0)) == JSON_TYPE_NULL) + { + ai_SetupLootFilters(oPlayer, oAssociate, sAssociateType); + } + else + { + SetLocalInt(oAssociate, AI_MAX_LOOT_WEIGHT, JsonGetInt(JsonArrayGet(jLootFilters, 0))); + SetLocalInt(oAssociate, sLootFilterVarname, JsonGetInt(JsonArrayGet(jLootFilters, 1))); + int nIndex; + for(nIndex = 2; nIndex < 20; nIndex++) + { + SetLocalInt(oAssociate, AI_MIN_GOLD_ + IntToString(nIndex), JsonGetInt(JsonArrayGet(jLootFilters, nIndex))); + } + } + // ********** Plugins ************ + // These are pulled straight from the database. + // ********** Locations ********** + json jLocations = ai_GetAssociateDbJson(oPlayer, sAssociateType, "locations"); + if(JsonGetType(JsonObjectGet(jLocations, sAssociateType + AI_WIDGET_NUI)) == JSON_TYPE_NULL) + { + ai_SetupLocations(oPlayer, oAssociate, sAssociateType); + } + // They are always pulled from the database, so no copies to local variables. +} +void ai_SetupDMData(object oPlayer, string sName) +{ + //ai_Debug("0i_main", "870", GetName(oPlayer) + " is initializing DM data."); + ai_CheckDMDataAndInitialize(oPlayer); + // ********** Buttons ********** + json jButtons = JsonArray(); + jButtons = JsonArrayInsert(jButtons, JsonInt(0)); // DM Widget Buttons. + ai_SetCampaignDbJson("buttons", jButtons, sName, AI_DM_TABLE); + // ********** Plugins ************ + // These are pulled straight from the database. + json jPlugins = JsonArray(); + ai_SetCampaignDbJson("plugins", jPlugins, sName, AI_DM_TABLE); + // ********** Locations ********** + json jLocations = JsonObject(); + json jNUI = JsonObject(); + jNUI = JsonObjectSet(jNUI, "x", JsonFloat(-1.0)); + jNUI = JsonObjectSet(jNUI, "y", JsonFloat(-1.0)); + jLocations = JsonObjectSet(jLocations, AI_MAIN_NUI, jNUI); + jLocations = JsonObjectSet(jLocations, AI_PLUGIN_NUI, jNUI); + jNUI = JsonObjectSet(jLocations, "x", JsonFloat(1.0)); + jNUI = JsonObjectSet(jLocations, "y", JsonFloat(1.0)); + jLocations = JsonObjectSet(jLocations, AI_WIDGET_NUI, jNUI); + ai_SetCampaignDbJson("locations", jLocations, sName, AI_DM_TABLE); + // ********** Options ********** + json jOptions = JsonArray(); + ai_SetCampaignDbJson("options", jOptions, sName, AI_DM_TABLE); + // ********** SaveSlots ********** + json jSaveSlots = JsonObject(); + ai_SetCampaignDbJson("saveslots", jSaveSlots, sName, AI_DM_TABLE); +} +void ai_CheckDMData(object oPlayer) +{ + //ai_Debug("0i_main", "898", "Checking data for DM: " + GetName(oPlayer)); + string sName = ai_RemoveIllegalCharacters(GetName(oPlayer)); + // ********** Buttons ********** + json jButtons = ai_GetCampaignDbJson("buttons", sName, AI_DM_TABLE); + // if there is no saved AImodes then set the defaults. + if(JsonGetType(JsonArrayGet(jButtons, 0)) == JSON_TYPE_NULL) + { + ai_SetupDMData(oPlayer, sName); + } + else + { + //ai_Debug("0i_main", "909", GetName(oPlayer) + " is loading DM data from the database."); + // Get data from the database and place on to the associates and player. + // ********** Buttons ********** + json jButtons = ai_GetCampaignDbJson("buttons", sName, AI_DM_TABLE); + if(JsonGetType(JsonArrayGet(jButtons, 0)) == JSON_TYPE_NULL) + { + ai_SetupDMData(oPlayer, sName); + } + SetLocalInt(oPlayer, sDMWidgetButtonVarname, JsonGetInt(JsonArrayGet(jButtons, 0))); + // ********** Associate Command Buttons ********** + int nWidgetButtons = JsonGetInt(JsonArrayGet(jButtons, 0)); + SetLocalInt(oPlayer, sDMWidgetButtonVarname, nWidgetButtons); + // ********** Plugins ************ + // These are pulled straight from the database. + // ********** Locations ********** + // These are pulled straight from the database. + // ********** Options ********** + // ********** SaveSltos ********** + } +} +json ai_Plugin_Add(object oPC, json jPlugins, string sPluginScript) +{ + if(ResManGetAliasFor(sPluginScript, RESTYPE_NCS) == "") + { + ai_SendMessages("The script (" + sPluginScript + ") was not found by ResMan!", AI_COLOR_RED, oPC); + return jPlugins; + } + int nIndex; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + if(JsonGetString(JsonArrayGet(jPlugin, 0)) == sPluginScript) + { + ai_SendMessages("Plugin (" + sPluginScript + ") is already installed!", AI_COLOR_RED, oPC); + return jPlugins; + } + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + SetLocalInt(oPC, AI_ADD_PLUGIN, TRUE); + SetLocalJson(oPC, AI_JSON_PLUGINS, jPlugins); + ExecuteScript(sPluginScript, oPC); + int nPluginSet = GetLocalInt(oPC, AI_PLUGIN_SET); + // Setting AI_PLUGIN_SET to -1 means the plugin failed to load. + if(nPluginSet == -1) return jPlugins; + if(nPluginSet) + { + jPlugin = GetLocalJson(oPC, AI_JSON_PLUGINS); + jPlugins = JsonArrayInsert(jPlugins, jPlugin); + } + else + { + jPlugin = JsonArray(); + jPlugin = JsonArrayInsert(jPlugin, JsonString(sPluginScript)); + jPlugin = JsonArrayInsert(jPlugin, JsonBool(FALSE)); + jPlugin = JsonArrayInsert(jPlugin, JsonString(sPluginScript)); + int nCount = JsonGetLength(jPlugins) + 1; + string sIcon = "is_summon" + IntToString(nCount); + jPlugin = JsonArrayInsert(jPlugin, JsonString(sIcon)); + jPlugins = JsonArrayInsert(jPlugins, jPlugin); + } + DeleteLocalInt(oPC, AI_ADD_PLUGIN); + DeleteLocalInt(oPC, AI_PLUGIN_SET); + DeleteLocalJson(oPC, AI_JSON_PLUGINS); + return jPlugins; +} +// Temporary function to addapt old plugin json to new plugin json. +json ai_CheckOldPluginJson(object oPC) +{ + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + int nIndex; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + // If the first array is not an array then this is the old version. + if(JsonGetType(jPlugin) != JSON_TYPE_ARRAY) + { + string sScript; + json jNewPlugins = JsonArray(); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sScript = JsonGetString(jPlugin); + if(sScript != "") jNewPlugins = ai_Plugin_Add(oPC, jNewPlugins, sScript); + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + + } + ai_SetAssociateDbJson(oPC, "pc", "plugins", jNewPlugins); + return jNewPlugins; + } + return jPlugins; +} +json ai_UpdatePluginsForPC(object oPC) +{ + // Check if the server is running or single player. + if(!AI_SERVER) return ai_CheckOldPluginJson(oPC); + int nJsonType, nCounter, nIndex, bWidget, bAllow; + string sScript, sName, sIcon; + json jServerPlugins = ai_GetCampaignDbJson("plugins"); + json jPCPlugin, jPCPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + json jNewPCPlugins = JsonArray(); + json jServerPlugin = JsonArrayGet(jServerPlugins, nIndex); + while(JsonGetType(jServerPlugin) != JSON_TYPE_NULL) + { + bAllow = JsonGetInt(JsonArrayGet(jServerPlugin, 1)); + if(bAllow) + { + sName = JsonGetString(JsonArrayGet(jServerPlugin, 0)); + nCounter = 0; + jPCPlugin = JsonArrayGet(jPCPlugins, nCounter); + nJsonType = JsonGetType(jPCPlugin); + while(nJsonType != JSON_TYPE_NULL) + { + if(sName == JsonGetString(JsonArrayGet(jPCPlugin, 0))) + { + // Boolean - Add to widget. + bWidget = JsonGetInt(JsonArrayGet(jPCPlugin, 1)); + jServerPlugin = JsonArraySet(jServerPlugin, 1, JsonBool(bWidget)); + break; + } + jPCPlugin = JsonArrayGet(jPCPlugins, ++nCounter); + nJsonType = JsonGetType(jPCPlugin); + } + if(nJsonType == JSON_TYPE_NULL) + { + jServerPlugin = JsonArraySet(jServerPlugin, 1, JsonBool(FALSE)); + } + jNewPCPlugins = JsonArrayInsert(jNewPCPlugins, jServerPlugin); + } + jServerPlugin = JsonArrayGet(jServerPlugins, ++nIndex); + } + ai_SetAssociateDbJson(oPC, "pc", "plugins", jNewPCPlugins); + return jNewPCPlugins; +} +json ai_UpdatePluginsForDM(object oPC) +{ + int nJsonType, nCounter, nIndex, bWidget, bAllow; + string sName, sIcon, sDbName = ai_RemoveIllegalCharacters(GetName(oPC)); + json jServerPlugins = ai_GetCampaignDbJson("plugins"); + ai_CheckDMDataAndInitialize(oPC); + json jDMPlugin, jDMPlugins = ai_GetCampaignDbJson("plugins", sDbName, AI_DM_TABLE); + json jNewDMPlugins = JsonArray(); + json jServerPlugin = JsonArrayGet(jServerPlugins, nIndex); + while(JsonGetType(jServerPlugin) != JSON_TYPE_NULL) + { + sName = JsonGetString(JsonArrayGet(jServerPlugin, 0)); + nCounter = 0; + jDMPlugin = JsonArrayGet(jDMPlugins, nCounter); + nJsonType = JsonGetType(jDMPlugin); + while(nJsonType != JSON_TYPE_NULL) + { + if(sName == JsonGetString(JsonArrayGet(jDMPlugin, 0))) + { + // Boolean - Add to widget. + bWidget = JsonGetInt(JsonArrayGet(jDMPlugin, 1)); + jServerPlugin = JsonArraySet(jServerPlugin, 1, JsonBool(bWidget)); + break; + } + jDMPlugin = JsonArrayGet(jDMPlugins, ++nCounter); + nJsonType = JsonGetType(jDMPlugin); + } + if(nJsonType == JSON_TYPE_NULL) + { + jServerPlugin = JsonArraySet(jServerPlugin, 1, JsonBool(FALSE)); + } + jNewDMPlugins = JsonArrayInsert(jNewDMPlugins, jServerPlugin); + jServerPlugin = JsonArrayGet(jServerPlugins, ++nIndex); + } + ai_SetCampaignDbJson("plugins", jNewDMPlugins, sDbName, AI_DM_TABLE); + return jNewDMPlugins; +} +void ai_StartupPlugins(object oPC) +{ + SetLocalInt(oPC, AI_STARTING_UP, TRUE); + int bUpdatePlugins; + string sScript; + json jPlugins; + if(GetIsDM(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); + int nIndex; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sScript = JsonGetString(JsonArrayGet(jPlugin, 0)); + ExecuteScript(sScript, oPC); + // -1 means if failed to load so lets make sure to remove it from the list. + if(GetLocalInt(oPC, AI_PLUGIN_SET) == -1) + { + jPlugins = JsonArrayDel(jPlugins, nIndex); + bUpdatePlugins = TRUE; + nIndex--; + } + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + if(bUpdatePlugins) ai_SetAssociateDbJson(oPC, "pc", "plugins", jPlugins); + DeleteLocalInt(oPC, AI_STARTING_UP); +} diff --git a/_module/nss/0i_menus.nss b/_module/nss/0i_menus.nss new file mode 100644 index 00000000..756797cb --- /dev/null +++ b/_module/nss/0i_menus.nss @@ -0,0 +1,4935 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_menus +//////////////////////////////////////////////////////////////////////////////// + Include script for handling NUI menus. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" +#include "0i_associates" +// Maximum number of Plugins allowed on the players widget. +const int WIDGET_MAX_PLUGINS = 5; + +// Set one of the BTN_* "Widget" bitwise constants on oPlayer to bValid. +void ai_SetWidgetButton(object oPlayer, int nButton, object oAssociate, string sAssociateType, int bOn = TRUE); +// Return if nButton is set on oPlayer. Uses the BTN_* "Widget" bitwise constants. +int ai_GetWidgetButton(object oPlayer, int nButton, object oAssociate, string sAssociateType); +// Set one of the BTN_AI_* bitwise constants on oPlayer to bValid. +void ai_SetAIButton(object oPlayer, int nButton, object oAssociate, string sAssociateType, int bOn = TRUE); +// Return if nButton is set on oPlayer. Uses the BTN_AI_* "Widget" bitwise constants. +int ai_GetAIButton(object oPlayer, int nButton, object oAssociate, string sAssociateType); +// Set one of the BTN2_AI_* bitwise constants on oPlayer to bValid. +void ai_SetAIButton2(object oPlayer, int nButton, object oAssociate, string sAssociateType, int bOn = TRUE); +// Return if nButton is set on oPlayer. Uses the BTN2_AI_* "Widget" bitwise constants. +int ai_GetAIButton2(object oPlayer, int nButton, object oAssociate, string sAssociateType); +// Creates the json array required to build a companion drop down box for +// Animal Companions or Familiars. +// sCompanion2da should be either "hen_companion" or "hen_familiar". +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. +void ai_CreateAssociateCommandNUI(object oPC, object oAssociate); +// Creates an associates AI NUI. +void ai_CreateAssociateAINUI(object oPC, object oAssociate); +// Creates a widget for the player or associate. +void ai_CreateWidgetNUI(object oPC, object oAssociate); +// Creates the Loot filter menu. +void ai_CreateLootFilterNUI(object oPC, object oAssociate); +// Creates the Plugin Manager menu. +void ai_CreatePluginNUI(object oPC); +// Creates the Spell menu that selects the spells to go on the Spell Widget. +void ai_CreateQuickWidgetSelectionNUI(object oPC, object oAssociate); +// Creates the Spell menu that lets the player to select the associates castable spells. +void ai_CreateSpellMemorizationNUI(object oPC, object oAssociate); +// Creates the spell description menu so a player can see what a spell does. +// If nSpell > 0 then use that value for the spells description. +void ai_CreateDescriptionNUI(object oPC, json jSpell, int nSpell = 0); + +string ai_GetRandomTip() +{ + int nRoll; + if(AI_SERVER) nRoll = Random(26); + else nRoll = Random(46); + return Get2DAString("ai_messages", "Text", nRoll); +} +void ai_SetWidgetButton(object oPlayer, int nButton, object oAssociate, string sAssociateType, int bOn = TRUE) +{ + int nWidgetButtons = GetLocalInt(oAssociate, sWidgetButtonsVarname); + json jButtons = ai_GetAssociateDbJson(oPlayer, sAssociateType, "buttons"); + if(bOn) nWidgetButtons = nWidgetButtons | nButton; + else nWidgetButtons = nWidgetButtons & ~nButton; + SetLocalInt(oAssociate, sWidgetButtonsVarname, nWidgetButtons); + jButtons = JsonArraySet(jButtons, 0, JsonInt(nWidgetButtons)); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "buttons", jButtons); +} +int ai_GetWidgetButton(object oPlayer, int nButton, object oAssociate, string sAssociateType) +{ + // This is the DM access switch that uses the same bitwise as the players + // to control what widget buttons they can use. + if(ai_GetDMWAccessButton(nButton)) return FALSE; + int nWidgetButtons = GetLocalInt(oAssociate, sWidgetButtonsVarname); + return nWidgetButtons & nButton; +} +void ai_SetAIButton(object oPlayer, int nButton, object oAssociate, string sAssociateType, int bOn = TRUE) +{ + int nAIButtons = GetLocalInt(oAssociate, sAIButtonsVarname); + json jButtons = ai_GetAssociateDbJson(oPlayer, sAssociateType, "buttons"); + if(bOn) nAIButtons = nAIButtons | nButton; + else nAIButtons = nAIButtons & ~nButton; + SetLocalInt(oAssociate, sAIButtonsVarname, nAIButtons); + jButtons = JsonArraySet(jButtons, 1, JsonInt(nAIButtons)); + ai_SetAssociateDbJson(oPlayer, sAssociateType, "buttons", jButtons); +} +int ai_GetAIButton(object oPlayer, int nButton, object oAssociate, string sAssociateType) +{ + // This is the DM access switch that uses the same bitwise as the players + // to control what AI widget buttons they can use. + if(ai_GetDMAIAccessButton(nButton)) return FALSE; + int nAIButtons = GetLocalInt(oAssociate, sAIButtonsVarname); + return nAIButtons & nButton; +} +json ai_CreateAIScriptJson(object oPC) +{ + json jScript = JsonArrayInsert(JsonArray(), NuiComboEntry("", 0)); + int nNth = 1; + string sScript = ResManFindPrefix("ai_a_", RESTYPE_NCS, nNth); + while(sScript != "") + { + jScript = JsonArrayInsert(jScript, NuiComboEntry(sScript, nNth)); + sScript = ResManFindPrefix("ai_a_", RESTYPE_NCS, ++nNth); + } + return jScript; +} +json ai_CreateCompanionJson(object oPC, string sCompanion2da) +{ + int nCnt, nMaxRowCount = Get2DARowCount(sCompanion2da); + string sName; + json jCompanion = JsonArray(); + while(nCnt < nMaxRowCount) + { + sName = GetStringByStrRef(StringToInt(Get2DAString(sCompanion2da, "STRREF", nCnt))); + jCompanion = JsonArrayInsert(jCompanion, NuiComboEntry(sName, nCnt++)); + } + return JsonArrayInsert(jCompanion, NuiComboEntry("Random", nCnt)); +} +string ai_GetSpellIconAttributes(object oCaster, int nMetaMagic, int nDomain) +{ + string sAttributeText; + if(nMetaMagic != METAMAGIC_ANY && nMetaMagic != METAMAGIC_NONE) + { + if(nMetaMagic == METAMAGIC_EXTEND) sAttributeText = "X"; + if(nMetaMagic == METAMAGIC_EMPOWER) sAttributeText = "P"; + if(nMetaMagic == METAMAGIC_MAXIMIZE) sAttributeText = "M"; + if(nMetaMagic == METAMAGIC_QUICKEN) sAttributeText = "Q"; + if(nMetaMagic == METAMAGIC_SILENT) sAttributeText = "I"; + if(nMetaMagic == METAMAGIC_STILL) sAttributeText = "T"; + } + else sAttributeText = ""; + 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. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + int nMonsterAI = (ResManGetAliasFor("ai_default", RESTYPE_NCS) != ""); + int nAssociateAI = (ResManGetAliasFor("ai_a_default", RESTYPE_NCS) != ""); + string sText = " [Single player]"; + if(AI_SERVER) sText = " [Server]"; + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 500 / 73 + json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, PHILOS_VERSION + sText, "lbl_version ", 510.0f, 20.0f, NUI_HALIGN_CENTER); + 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)); + // 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"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 ******************************************************************* 500 / 157 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, "MODULE RULES", "lbl_ai_rules", 200.0f, 20.0f, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + float fHeight = 157.0; + // Row 5 ******************************************************************* 500 / --- (28) + // Make the AI options a Group. + json jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_max_henchman", 2, FALSE, 30.0f, 20.0f, "txt_max_henchman_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "Max number of henchmen that is allowed in your party.", "lbl_max_hench", 416.0f, 20.0f, NUI_HALIGN_LEFT, 0, -1.0, "txt_max_henchman_tooltip"); + json jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_xp_scale", 3, FALSE, 40.0f, 20.0f, "txt_xp_scale_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "Modules experience scale.", "lbl_xp_scale", 175.0f, 20.0f, NUI_HALIGN_LEFT, 0, -1.0, "txt_xp_scale_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " scale to party.", "chbx_party_scale", 150.0, 20.0, "chbx_party_scale_tooltip"); + jGroupRow = CreateButton(jGroupRow, "Default", "btn_default_xp", 70.0f, 20.0f, -1.0, "btn_default_xp_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight += 78.0; + if(nMonsterAI || nAssociateAI) + { + jGroupRow = CreateCheckBox(JsonArray(), " Creatures will use advanced combat movement.", "chbx_advanced_movement", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Use item level restrictions for creatures [Default is off].", "chbx_ilr", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Creatures can use the skill Use Magic Device.", "chbx_umd", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Creatures can use Healing kits.", "chbx_use_healingkits", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Moral checks, wounded creatures may flee during combat.", "chbx_moral", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), " Spells the AI will not use:", "lbl_restrict_spells", 190.0, 20.0, NUI_HALIGN_LEFT); + jGroupRow = CreateCheckBox(jGroupRow, " Darkness", "chbx_darkness", 90.0, 20.0, "chbx_darkness_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " Dispels", "chbx_dispels", 90.0, 20.0, "chbx_dispels_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " Time Stop", "chbx_timestop", 90.0, 20.0, "chbx_timestop_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight += 196.0; + } + if(nMonsterAI) + { + jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_ai_difficulty", 3, FALSE, 40.0f, 20.0f, "txt_ai_difficulty_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "% chance monsters will attack the weakest target.", "lbl_ai_difficulty", 406.0f, 20.0f, NUI_HALIGN_LEFT, 0, -1.0, "txt_ai_difficulty_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + 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); + 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)); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters can use tactics (ambush, defensive, flanker, etc).", "chbx_ambush_monsters", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "Add ", "lbl_inc_enc", 30.0, 20.0, NUI_HALIGN_LEFT, 0, -1.0); + jGroupRow = CreateTextEditBox(jGroupRow, "sPlaceHolder", "txt_inc_enc", 4, FALSE, 55.0f, 20.0f, "txt_inc_enc_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "monsters per spawned encounter monster.", "lbl_inc_enc", 357.0, 20.0, NUI_HALIGN_LEFT, NUI_VALIGN_MIDDLE, 0.0, "txt_inc_enc_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_inc_hp", 3, FALSE, 40.0f, 20.0f, "txt_inc_hp_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "% increase in all monster's hitpoints.", "lbl_inc_hp", 406.0, 20.0, NUI_HALIGN_LEFT); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "***** WARNING! The options below may break the module! *****", "lbl_warning", 450.0f, 20.0f, NUI_HALIGN_LEFT, 0, 0.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters can wander upto ", "chbx_wander", 220.0, 20.0, "chbx_warning_tooltip"); + jGroupRow = CreateTextEditBox(jGroupRow, "sPlaceHolder", "txt_wander_distance", 2, FALSE, 35.0f, 20.0f, "chbx_warning_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "meters and ", "lbl_wander_distance", 80.0f, 20.0f, NUI_HALIGN_LEFT, NUI_VALIGN_MIDDLE, 0.0, "chbx_warning_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, "open doors.", "chbx_open_doors", 100.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters can summon companions.", "chbx_companions", 450.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Summoned associates to remain after masters death.", "chbx_perm_assoc", 450.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Make enemy corpses remain.", "chbx_corpses_stay", 450.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "", "lbl_perc_dist", 450.0f, 20.0f, NUI_HALIGN_LEFT, 0, 0.0, "lbl_perc_dist_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight += 336.0; + } + jRow = JsonArrayInsert(JsonArray(), NuiGroup(NuiCol(jGroupCol))); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Get the window location to restore it from the database. + json jLocations = ai_GetAssociateDbJson(oPC, "pc", "locations"); + jLocations = JsonObjectGet(jLocations, AI_MAIN_NUI); + float fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sName = 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"); + // Save the associate to the nui for use in 0e_nui + json jData = JsonArray(); + jData = JsonArrayInsert(jData, JsonString(ObjectToString(oPC))); + NuiSetUserData(oPC, nToken, jData); + object oModule = GetModule(); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set all binds, events, and watches. + // Row 1 - Version label. + // Row 2 + int nUsing; + // 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)); + // 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)); + NuiSetBind(oPC, nToken, "btn_action_ghost_tooltip", JsonString(" Allows associates to move through creatures while in command mode.")); + 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.")); + // Row 3 Label for AI RULES + // Row 4 + NuiSetBind(oPC, nToken, "txt_max_henchman", JsonString(IntToString(GetLocalInt(oModule, AI_RULE_MAX_HENCHMAN)))); + NuiSetBindWatch (oPC, nToken, "txt_max_henchman", TRUE); + NuiSetBind(oPC, nToken, "txt_max_henchman_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_max_henchman_tooltip", JsonString(" Set max number of henchman allowed (1-12).")); + NuiSetBind(oPC, nToken, "txt_xp_scale", JsonString(IntToString(GetModuleXPScale()))); + NuiSetBindWatch (oPC, nToken, "txt_xp_scale", TRUE); + NuiSetBind(oPC, nToken, "txt_xp_scale_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_xp_scale_tooltip", JsonString(" Set the modules XP scale (0 - 200) Normal D&D is 10.")); + NuiSetBind(oPC, nToken, "chbx_party_scale_check", JsonBool(GetLocalInt(oModule, AI_RULE_PARTY_SCALE))); + NuiSetBindWatch(oPC, nToken, "chbx_party_scale_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_party_scale_event", JsonBool(TRUE)); + sText = IntToString(GetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP)); + NuiSetBind(oPC, nToken, "chbx_party_scale_tooltip", JsonString(" PEPS adjusts your XP based on party size from (" + sText + ").")); + NuiSetBind(oPC, nToken, "btn_default_xp_event", JsonBool(TRUE)); + sText = IntToString(GetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE)); + NuiSetBind(oPC, nToken, "btn_default_xp_tooltip", JsonString(" Reset the Modules XP to (" + sText + ").")); + NuiSetBind(oPC, nToken, "chbx_warning_tooltip", JsonString(" ** This will break some modules! ** See Readme for issues!")); + if(nMonsterAI) + { + 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))); + NuiSetBindWatch(oPC, nToken, "chbx_buff_monsters_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_buff_monsters_event", JsonBool(TRUE)); + 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)); + NuiSetBind(oPC, nToken, "chbx_ambush_monsters_check", JsonBool(GetLocalInt(oModule, AI_RULE_AMBUSH))); + NuiSetBindWatch(oPC, nToken, "chbx_ambush_monsters_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ambush_monsters_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_companions_check", JsonBool(GetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS))); + NuiSetBindWatch(oPC, nToken, "chbx_companions_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_companions_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_perm_assoc_check", JsonBool(GetLocalInt(oModule, AI_RULE_PERM_ASSOC))); + string sModuleName = GetModuleName(); + if(!GetLocalInt(oModule, AI_USING_PRC) && + (sModuleName != "Neverwinter Nights - Infinite Dungeons" || + sModuleName != "Infinite Dungeons [PRC8]")) + { + NuiSetBindWatch(oPC, nToken, "chbx_perm_assoc_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_perm_assoc_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_corpses_stay_check", JsonBool(GetLocalInt(oModule, AI_RULE_CORPSES_STAY))); + NuiSetBindWatch(oPC, nToken, "chbx_corpses_stay_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_corpses_stay_event", JsonBool(TRUE)); + } + NuiSetBind(oPC, nToken, "txt_perception_distance", JsonString(FloatToString(GetLocalFloat(oModule, AI_RULE_PERCEPTION_DISTANCE), 0, 0))); + NuiSetBindWatch(oPC, nToken, "txt_perception_distance", TRUE); + NuiSetBind(oPC, nToken, "txt_perception_distance_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_perception_distance_tooltip", JsonString(" Range [10 to 60 meters] from the player.")); + NuiSetBindWatch(oPC, nToken, "lbl_perc_dist", TRUE); + int nPercDist = GetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE); + if(nPercDist < 8 || nPercDist > 11) + { + nPercDist = 11; + SetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE, 11); + } + if(nPercDist == 8) sText = " Monster perception: Short [10 Sight / 10 Listen]"; + else if(nPercDist == 9) sText = " Monster perception: Medium [20 Sight / 20 Listen]"; + else if(nPercDist == 10) sText = " Monster perception: Long [35 Sight / 20 Listen]"; + else sText = " Monster perception: Default [Monster's default values]"; + NuiSetBind(oPC, nToken, "lbl_perc_dist_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "lbl_perc_dist_tooltip", JsonString(" Use the mouse wheel to change values.")); + int bWander = GetLocalInt(oModule, AI_RULE_WANDER); + NuiSetBind(oPC, nToken, "chbx_wander_check", JsonBool(bWander)); + NuiSetBindWatch(oPC, nToken, "chbx_wander_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_wander_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_wander_distance", JsonString(FloatToString(GetLocalFloat(oModule, AI_RULE_WANDER_DISTANCE), 0, 0))); + NuiSetBindWatch(oPC, nToken, "txt_wander_distance", TRUE); + NuiSetBind(oPC, nToken, "txt_wander_distance_event", JsonBool(bWander)); + NuiSetBind(oPC, nToken, "chbx_open_doors_check", JsonBool(GetLocalInt(oModule, AI_RULE_OPEN_DOORS))); + NuiSetBindWatch(oPC, nToken, "chbx_open_doors_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_open_doors_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_open_doors_tooltip", JsonString(" This allows monsters to open doors to hunt you down!")); + NuiSetBind(oPC, nToken, "txt_inc_enc_tooltip", JsonString(" Spawns one extra monster per counter above 1. Adds value to counter per encounter monster spawned.")); + NuiSetBind(oPC, nToken, "txt_inc_enc", JsonString(FloatToString(GetLocalFloat(oModule, AI_INCREASE_ENC_MONSTERS), 0, 2))); + NuiSetBindWatch(oPC, nToken, "txt_inc_enc", TRUE); + 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_event", JsonBool(TRUE)); + } + if(nMonsterAI || nAssociateAI) + { + NuiSetBind(oPC, nToken, "chbx_moral_check", JsonBool(GetLocalInt(oModule, AI_RULE_MORAL_CHECKS))); + NuiSetBindWatch (oPC, nToken, "chbx_moral_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_moral_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_advanced_movement_check", JsonBool(GetLocalInt(oModule, AI_RULE_ADVANCED_MOVEMENT))); + NuiSetBindWatch (oPC, nToken, "chbx_advanced_movement_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_advanced_movement_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_ilr_check", JsonBool(GetLocalInt(oModule, AI_RULE_ILR))); + NuiSetBindWatch (oPC, nToken, "chbx_ilr_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ilr_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_umd_check", JsonBool(GetLocalInt(oModule, AI_RULE_ALLOW_UMD))); + NuiSetBindWatch (oPC, nToken, "chbx_umd_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_umd_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_use_healingkits_check", JsonBool(GetLocalInt(oModule, AI_RULE_HEALERSKITS))); + NuiSetBindWatch (oPC, nToken, "chbx_use_healingkits_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_use_healingkits_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_darkness_check", JsonBool(ai_SpellRestricted(SPELL_DARKNESS))); + NuiSetBindWatch (oPC, nToken, "chbx_darkness_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_darkness_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_darkness_tooltip", JsonString(" AI will not use the Darkness spell in combat.")); + NuiSetBind(oPC, nToken, "chbx_dispels_check", JsonBool(ai_SpellRestricted(SPELL_DISPEL_MAGIC))); + NuiSetBindWatch (oPC, nToken, "chbx_dispels_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_dispels_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_dispels_tooltip", JsonString(" AI will not use any of the Dispel spells in combat.")); + NuiSetBind(oPC, nToken, "chbx_timestop_check", JsonBool(ai_SpellRestricted(SPELL_TIME_STOP))); + NuiSetBindWatch (oPC, nToken, "chbx_timestop_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_timestop_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_timestop_tooltip", JsonString(" AI will not use the Time Stop spell in combat.")); + } +} +void ai_CreateAssociateCommandNUI(object oPC, object oAssociate) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + int bRight, bLeft; + int bIsPC = ai_GetIsCharacter(oAssociate); + int bUsingPCAI = ResManGetAliasFor("xx_pc_1_hb", RESTYPE_NCS) != ""; + int bUsingHenchAI = ResManGetAliasFor("nw_ch_ac1", RESTYPE_NCS) != ""; + float fHeight = 73.0; + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 500 / 73 + json jRow = JsonArray(); + json jCol = JsonArray(); + // If all the AI buttons are blocked then don't load the menu. + if(GetLocalInt(GetModule(), sDMAIAccessVarname) != 203423743) + { + if(bIsPC) + { + if(bUsingPCAI || !AI_SERVER) + { + if(bUsingPCAI) + { + jRow = CreateButton(jRow, "AI Menu", "btn_ai_menu", 232.0, 20.0, -1.0, "btn_ai_menu_tooltip"); + } + if(!AI_SERVER) + { + jRow = CreateButton(jRow, "Main Menu", "btn_main_menu", 232.0, 20.0, -1.0, "btn_main_menu_tooltip"); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + } + else + { + if(bUsingHenchAI) + { + jRow = CreateButton(jRow, "AI Menu", "btn_ai_menu", 232.0, 20.0, -1.0, "btn_ai_menu_tooltip"); + } + jRow = CreateButtonSelect(jRow, "", "btn_widget_onoff", 232.0, 20.0, "btn_widget_onoff_tooltip"); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + } + // Row 2 ******************************************************************* 500 / 101 + jRow = JsonArray(); + jRow = CreateButtonSelect(jRow, "Lock Widget", "btn_widget_lock", 154.0, 20.0, "btn_widget_lock_tooltip"); + jRow = CreateButton(jRow, "Copy Settings", "btn_copy_settings", 154.0, 20.0, -1.0, "btn_copy_settings_tooltip"); + 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_CMD_ACTION); + bLeft = !ai_GetDMWAccessButton(BTN_CMD_GUARD); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "", "btn_cmd_action", 200.0, 20.0, -1.0, "btn_cmd_action_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_action", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "", "btn_cmd_guard", 200.0, 20.0, -1.0, "btn_cmd_guard_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_guard", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 4 ******************************************************************* 500 / 157 + jRow = JsonArray(); + bRight = !ai_GetDMWAccessButton(BTN_CMD_HOLD); + bLeft = !ai_GetDMWAccessButton(BTN_CMD_ATTACK); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "", "btn_cmd_hold", 200.0, 20.0, -1.0, "btn_cmd_hold_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_hold", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "", "btn_cmd_attack", 200.0, 20.0, -1.0, "btn_cmd_attack_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_attack", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 5 ******************************************************************* 500 / 213 + bRight = !ai_GetDMWAccessButton(BTN_CMD_FOLLOW); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_FOLLOW_TARGET); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "", "btn_cmd_follow", 200.0, 20.0, -1.0, "btn_cmd_follow_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_follow", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Select follow target", "btn_follow_target", 200.0, 20.0, -1.0, "btn_follow_target_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_follow_target", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 6 ******************************************************************* 500 / 185 + if(bIsPC) + { + bRight = !ai_GetDMWAccessButton(BTN_CMD_SEARCH); + bLeft = !ai_GetDMWAccessButton(BTN_CMD_STEALTH); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "All Search Mode", "btn_cmd_search", 200.0, 20.0, -1.0, "btn_cmd_search_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_search", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "All Stealth Mode", "btn_cmd_stealth", 200.0, 20.0, -1.0, "btn_cmd_stealth_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_stealth", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + } + // Row 7 ******************************************************************* 500 / 241 + bRight = !ai_GetDMWAccessButton(BTN_CMD_AI_SCRIPT); + bLeft = !ai_GetDMWAccessButton(BTN_CMD_PLACE_TRAP); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Combat Tactics", "btn_cmd_ai_script", 200.0, 20.0, -1.0, "btn_cmd_ai_script_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_ai_script", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Place a Trap", "btn_cmd_place_trap", 200.0, 20.0, -1.0, "btn_cmd_place_trap_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_place_trap", 25.0, 20.0); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 8 ******************************************************************* 500 / --- + int bMemorize = ai_GetIsSpellCaster(oAssociate); + int bSpellbook = ai_GetIsSpellBookRestrictedCaster(oAssociate); + bRight = !ai_GetDMWAccessButton(BTN_CMD_SPELL_WIDGET); + bLeft = !ai_GetDMWAccessButton(BTN_DM_CMD_MEMORIZE); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Set Quick Widget", "btn_quick_widget", 200.0, 20.0, -1.0, "btn_quick_widget_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_quick_widget", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) // Memorizes their spells. + { + if(bMemorize == 2 && bSpellbook) + { + jRow = CreateButton(jRow, "Memorize Spells", "btn_spell_memorize", 114.0, 20.0, -1.0, "btn_spell_memorize_tooltip"); + jRow = CreateButton(jRow, "Known Spells", "btn_spell_known", 110.0, 20.0, -1.0, "btn_spell_known_tooltip"); + } + else if(bMemorize == 2) + { + jRow = CreateButton(jRow, "Set Memorize Spells", "btn_spell_memorize", 200.0, 20.0, -1.0, "btn_spell_memorize_tooltip"); + jRow = CreateLabel(jRow, "", "blank_label_1", 25.0, 20.0); + } + else if(bSpellbook && !ai_GetIsCharacter(oAssociate)) + { + jRow = CreateButton(jRow, "Set Known Spells", "btn_spell_known", 200.0, 20.0, -1.0, "btn_spell_known_tooltip"); + jRow = CreateLabel(jRow, "", "blank_label_1", 25.0, 20.0); + } + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 9 ******************************************************************* 500 / 269 + bRight = !ai_GetDMWAccessButton(BTN_BUFF_SHORT); + bLeft = !ai_GetDMWAccessButton(BTN_BUFF_LONG); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Cast Short Buff spells", "btn_buff_short", 200.0, 20.0, -1.0, "btn_buff_short_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_short", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Cast Long Buff spells", "btn_buff_long", 200.0, 20.0, -1.0, "btn_buff_long_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_long", 25.0, 20.0); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 10 ******************************************************************* 500 / 297 + bRight = !ai_GetDMWAccessButton(BTN_BUFF_ALL); + bLeft = !ai_GetDMWAccessButton(BTN_BUFF_REST); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Cast All Buff spells", "btn_buff_all", 200.0, 20.0, -1.0, "btn_buff_all_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_all", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Buff after resting", "btn_buff_rest", 200.0, 20.0, -1.0, "btn_buff_rest_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_rest", 25.0, 20.0); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 11 ******************************************************************* 500 / 325 + bRight = !ai_GetDMWAccessButton(BTN_CMD_JUMP_TO); + bLeft = !ai_GetDMWAccessButton(BTN_CMD_GHOST_MODE); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "", "btn_jump_to", 200.0, 20.0, -1.0, "btn_jump_to"); + jRow = CreateCheckBox(jRow, "", "chbx_jump_to", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Ghost Mode", "btn_ghost_mode", 200.0, 20.0, -1.0, "btn_ghost_mode_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ghost_mode", 25.0, 20.0); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 12 ****************************************************************** 500 / 353 + bRight = !ai_GetDMWAccessButton(BTN_CMD_CAMERA); + bLeft = !ai_GetDMWAccessButton(BTN_CMD_INVENTORY); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Toggle Camera Focus", "btn_camera", 200.0, 20.0, -1.0, "btn_camera_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_camera", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Open/Close Inventory", "btn_inventory", 200.0, 20.0, -1.0, "btn_inventory_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_inventory", 25.0, 20.0); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 13 ******************************************************************* 500 / --- + int bFamiliar = GetHasFeat(FEAT_SUMMON_FAMILIAR, oAssociate, TRUE); + if(!ai_GetDMWAccessButton(BTN_CMD_FAMILIAR) && bFamiliar) + { + jRow = JsonArray(); + jRow = CreateLabel(jRow, "", "lbl_familiar_type", 225.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateLabel(jRow, "", "lbl_familiar_name", 225.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + // Row 14 ******************************************************************* 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); + jRow = CreateTextEditBox(jRow, "txtbox", "txt_familiar_name", 50, FALSE, 178.0, 20.0); + jRow = CreateButton(jRow, "", "btn_familiar_name", 55.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 15 ******************************************************************* 500 / --- + int bCompanion = GetHasFeat(FEAT_ANIMAL_COMPANION, oAssociate, TRUE); + if(!ai_GetDMWAccessButton(BTN_CMD_COMPANION) && bCompanion) + { + jRow = JsonArray(); + jRow = CreateLabel(jRow, "", "lbl_companion_type", 225.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateLabel(jRow, "", "lbl_companion_name", 225.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + // Row 16 ******************************************************************* 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); + jRow = CreateTextEditBox(jRow, "txtbox", "txt_companion_name", 50, FALSE, 178.0, 20.0); + jRow = CreateButton(jRow, "", "btn_companion_name", 55.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + } + // Row 17+ ****************************************************************** 500 / --- + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jPCPlugins; + if(bIsPC) + { + jPCPlugins = ai_UpdatePluginsForPC(oPC); + // Set the plugins the player can use. + int nIndex; + string sButton, sName; + json jPlugin = JsonArrayGet(jPCPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + jRow = JsonArray(); + sButton = IntToString(nIndex); + sName = JsonGetString(JsonArrayGet(jPlugin, 2)); + jRow = CreateButton(jRow, sName, "btn_plugin_" + sButton, 200.0f, 20.0f, -1.0, "btn_plugin_" + sButton + "_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_plugin_" + sButton, 25.0, 20.0, "chbx_plugin_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jPlugin = JsonArrayGet(jPCPlugins, ++nIndex); + if(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + sName = JsonGetString(JsonArrayGet(jPlugin, 2)); + jRow = CreateButton(jRow, sName, "btn_plugin_" + sButton, 200.0f, 20.0f, -1.0, "btn_plugin_" + sButton + "_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_plugin_" + sButton, 25.0, 20.0, "chbx_plugin_tooltip"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + else + { + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + break; + } + jPlugin = JsonArrayGet(jPCPlugins, ++nIndex); + } + } + // Row 18 ****************************************************************** 500 / --- + jRow = JsonArray(); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateLabel(jRow, "", "lbl_info_1", 475.0, 20.0, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + // Get the window location to restore it from the database. + float fX, fY; + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + jLocations = JsonObjectGet(jLocations, sAssociateType + AI_COMMAND_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sName = 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", + fX, fY, 500.0, fHeight + 12.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Get which buttons are activated. + int bAIWidgetLock = ai_GetWidgetButton(oPC, BTN_WIDGET_LOCK, oAssociate, sAssociateType); + int bCmdAction = ai_GetWidgetButton(oPC, BTN_CMD_ACTION, oAssociate, sAssociateType); + int bCmdGuard = ai_GetWidgetButton(oPC, BTN_CMD_GUARD, oAssociate, sAssociateType); + int bCmdHold = ai_GetWidgetButton(oPC, BTN_CMD_HOLD, oAssociate, sAssociateType); + int bCmdSearch = ai_GetWidgetButton(oPC, BTN_CMD_SEARCH, oAssociate, sAssociateType); + int bCmdStealth = ai_GetWidgetButton(oPC, BTN_CMD_STEALTH, oAssociate, sAssociateType); + int bCmdAttack = ai_GetWidgetButton(oPC, BTN_CMD_ATTACK, oAssociate, sAssociateType); + int bCmdFollow = ai_GetWidgetButton(oPC, BTN_CMD_FOLLOW, oAssociate, sAssociateType); + int bFollowTarget = ai_GetAIButton(oPC, BTN_AI_FOLLOW_TARGET, oAssociate, sAssociateType); + int bCmdAIScript = ai_GetWidgetButton(oPC, BTN_CMD_AI_SCRIPT, oAssociate, sAssociateType); + int bCmdPlacetrap = ai_GetWidgetButton(oPC, BTN_CMD_PLACE_TRAP, oAssociate, sAssociateType); + int bSpellWidget = ai_GetWidgetButton(oPC, BTN_CMD_SPELL_WIDGET, oAssociate, sAssociateType); + int bBuffRest = ai_GetWidgetButton(oPC, BTN_BUFF_REST, oAssociate, sAssociateType); + int bBuffShort = ai_GetWidgetButton(oPC, BTN_BUFF_SHORT, oAssociate, sAssociateType); + int bBuffLong = ai_GetWidgetButton(oPC, BTN_BUFF_LONG, oAssociate, sAssociateType); + int bBuffAll = ai_GetWidgetButton(oPC, BTN_BUFF_ALL, oAssociate, sAssociateType); + int bJumpTo = ai_GetWidgetButton(oPC, BTN_CMD_JUMP_TO, oAssociate, sAssociateType); + int bGhostMode = ai_GetWidgetButton(oPC, BTN_CMD_GHOST_MODE, oAssociate, sAssociateType); + int bCamera = ai_GetWidgetButton(oPC, BTN_CMD_CAMERA, oAssociate, sAssociateType); + 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 bVertical = ai_GetWidgetButton(oPC, BTN_WIDGET_VERTICAL, oAssociate, sAssociateType); + // Save the associate to the nui for use in 0e_nui + json jData = JsonArray(); + jData = JsonArrayInsert(jData, JsonString(ObjectToString(oAssociate))); + NuiSetUserData(oPC, nToken, jData); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set all binds, events, and watches. + string sText; + // Row 1 + // If all the AI buttons are blocked then don't load the menu. + if(GetLocalInt(GetModule(), sDMAIAccessVarname) != 203423743) + { + if(bIsPC) + { + if(bUsingPCAI) + { + NuiSetBind(oPC, nToken, "btn_ai_menu_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_ai_menu_tooltip", JsonString(" " + sName + " AI options")); + } + 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) + { + NuiSetBind(oPC, nToken, "btn_main_menu_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_main_menu_tooltip", JsonString(" Module Options")); + } + } + else + { + if(bUsingHenchAI) + { + NuiSetBind(oPC, nToken, "btn_ai_menu_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_ai_menu_tooltip", JsonString(" " + sName + " AI options")); + } + 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)); + string sText2; + if(ai_GetWidgetButton(oPC, BTN_WIDGET_OFF, oAssociate, sAssociateType)) + { + sText = "Off"; sText2 = "on"; + NuiSetBind(oPC, nToken, "btn_widget_onoff", JsonBool(FALSE)); + } + else + { + sText = "On"; sText2 = "off"; + NuiSetBind(oPC, nToken, "btn_widget_onoff", JsonBool(TRUE)); + } + NuiSetBind(oPC, nToken, "btn_widget_onoff_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_widget_onoff_label", JsonString("Widget " + sText)); + NuiSetBind(oPC, nToken, "btn_widget_onoff_tooltip", JsonString( + " Turn " + sName + " widget " + sText2)); + } + } + // Row 2 + NuiSetBind(oPC, nToken, "btn_widget_lock_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_widget_lock", JsonBool(bAIWidgetLock)); + NuiSetBind(oPC, nToken, "btn_widget_lock_tooltip", JsonString( + " Locks " + sName + " widget to the current location.")); + NuiSetBind(oPC, nToken, "btn_widget_size_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_widget_size_tooltip", JsonString( + " Adjusts the size of " + sName + " widget buttons")); + NuiSetBind(oPC, nToken, "btn_vertical_widget_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_vertical_widget", JsonBool(bVertical)); + NuiSetBind(oPC, nToken, "btn_vertical_widget_tooltip", JsonString( + " " + sName + " widget will display vertically")); + // Row 3 + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_action_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_guard_check", JsonBool (bCmdGuard)); + 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 + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_hold_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_attack_check", JsonBool (bCmdAttack)); + 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 + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_follow_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_follow_target_check", JsonBool (bFollowTarget)); + 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 + if(bIsPC) + { + NuiSetBind(oPC, nToken, "chbx_cmd_search_check", JsonBool (bCmdSearch)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_search_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_cmd_search_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_search_event", JsonBool (TRUE)); + if(ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_SEARCH)) sText = " leave "; + else sText = " enter "; + NuiSetBind(oPC, nToken, "btn_cmd_search_tooltip", JsonString(" Everyone" + sText + "search mode")); + NuiSetBind(oPC, nToken, "chbx_cmd_stealth_check", JsonBool (bCmdStealth)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_stealth_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_cmd_stealth_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_stealth_event", JsonBool (TRUE)); + if(ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_STEALTH)) sText = " leave "; + else sText = " enter "; + NuiSetBind(oPC, nToken, "btn_cmd_stealth_tooltip", JsonString(" Everyone" + sText + "stealth mode")); + } + // Command labels + if(bIsPC) sText = " All "; + else sText = " "; + NuiSetBind(oPC, nToken, "btn_cmd_action_label", JsonString(sText + "Action")); + NuiSetBind(oPC, nToken, "btn_cmd_guard_label", JsonString(sText + "Guard Mode")); + NuiSetBind(oPC, nToken, "btn_cmd_hold_label", JsonString(sText + "Hold Mode")); + NuiSetBind(oPC, nToken, "btn_cmd_attack_label", JsonString(sText + "Normal Mode")); + NuiSetBind(oPC, nToken, "btn_cmd_follow_label", JsonString(sText + "Follow Mode")); + NuiSetBind(oPC, nToken, "btn_follow_target_label", JsonString(" Follow Target")); + float fRange = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE) + + StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oAssociate))); + string sRange = FloatToString(fRange, 0, 0); + if(bIsPC) + { + sText = " All associates"; + NuiSetBind(oPC, nToken, "btn_cmd_follow_tooltip", JsonString(sText + " enter follow mode")); + } + else + { + sText = " " + GetName(oAssociate); + NuiSetBind(oPC, nToken, "btn_cmd_follow_tooltip", JsonString(sText + " enter follow mode [" + sRange + " meters]")); + } + NuiSetBind(oPC, nToken, "btn_cmd_action_tooltip", JsonString(sText + " do actions")); + NuiSetBind(oPC, nToken, "btn_cmd_guard_tooltip", JsonString(sText + " enter guard mode")); + NuiSetBind(oPC, nToken, "btn_cmd_hold_tooltip", JsonString(sText + " enter hold mode")); + NuiSetBind(oPC, nToken, "btn_cmd_attack_tooltip", JsonString(sText + " enter normal mode")); + object oTarget = GetLocalObject(oAssociate, AI_FOLLOW_TARGET); + string sTarget; + if(oTarget != OBJECT_INVALID) sTarget = GetName(oTarget); + else + { + if(ai_GetIsCharacter(oAssociate)) sTarget = "nobody"; + else sTarget = GetName(oPC); + } + NuiSetBind(oPC, nToken, "btn_follow_target_tooltip", JsonString(" " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]")); + // Row 7 + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_event", JsonBool (TRUE)); + sText = " Using normal tactics"; + if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) != "") + { + string sScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + if(sScript == "ai_a_ambusher") sText = " Ambusher: Attacks from a hidden position"; + else if(sScript == "ai_a_flanker") sText = " Flanker: Attacks enemies engaged with allies"; + else if(sScript == "ai_a_peaceful") sText = " Peaceful: Avoids attacking any enemies if possible"; + else if(sScript == "ai_a_defensive") sText = " Defensive: Attacks then uses Expertise/Parry"; + else if(sScript == "ai_a_ranged") sText = " Ranged: Attacks from range as much as possible"; + else if(sScript == "ai_a_cntrspell") sText = " Counter Spell: Tries to counter enemy spells"; + } + else + { + if(GetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, oAssociate)) sText = "Using ambush tactics"; + else if(GetCombatCondition(X0_COMBAT_FLAG_COWARDLY, oAssociate)) sText = "Using coward tactics"; + else if(GetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, oAssociate)) sText = "Using defensive tactics"; + else if(GetCombatCondition(X0_COMBAT_FLAG_RANGED, oAssociate)) sText = "Using ranged tactics"; + } + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_tooltip", JsonString(sText)); + if(GetSkillRank(SKILL_SET_TRAP, oAssociate, TRUE) > 0) + { + 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 a trap at the location selected")); + } + // Row 8 + 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")); + 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)); + if(bMemorize == 2) // Memorizes their spells. + { + NuiSetBind(oPC, nToken, "btn_spell_memorize_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_spell_memorize_tooltip", JsonString( + " Change memorized spell list.")); + } + if(bSpellbook) // Change known spells. + { + NuiSetBind(oPC, nToken, "btn_spell_known_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_spell_known_tooltip", JsonString( + " Change known spell list.")); + } + // Row 9 + 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)); + NuiSetBind (oPC, nToken, "btn_buff_short_tooltip", JsonString ( + " Buff the party with short duration spells")); + 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 + 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")); + if(!bIsPC && ResManGetAliasFor("prc_ai_fam_percp", RESTYPE_NCS) == "") + { + NuiSetBind(oPC, nToken, "chbx_buff_rest_check", JsonBool (bBuffRest)); + NuiSetBindWatch (oPC, nToken, "chbx_buff_rest_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_buff_rest_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_buff_rest_event", JsonBool (TRUE)); + if(ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST)) sText = " [On] Turn buffing after resting off"; + else sText = " [Off] Turn buffing after resting on"; + NuiSetBind (oPC, nToken, "btn_buff_rest_tooltip", JsonString (sText)); + } + // Row 11 + 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)); + sText = GetName(oPC); + if(oPC == oAssociate) sName = "everyone"; + else sName = GetName(oAssociate); + NuiSetBind(oPC, nToken, "btn_jump_to_label", JsonString("Jump to " + sText)); + NuiSetBind(oPC, nToken, "btn_jump_to_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_jump_to_tooltip", JsonString ( + " Jump " + sName + " to " + sText)); + + NuiSetBind(oPC, nToken, "chbx_ghost_mode_check", JsonBool (bGhostMode)); + NuiSetBindWatch (oPC, nToken, "chbx_ghost_mode_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ghost_mode_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ghost_mode_event", JsonBool (TRUE)); + sText = "On"; + 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 + NuiSetBind(oPC, nToken, "chbx_camera_check", JsonBool (bCamera)); + NuiSetBindWatch (oPC, nToken, "chbx_camera_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_camera_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_camera_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_camera_tooltip", JsonString ( + " Toggle camera view for " + sName)); + 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 " + sName + " inventory")); + // Row 13 & 14 + if(bFamiliar) + { + NuiSetBind(oPC, nToken, "chbx_familiar_check", JsonBool(bBtnFamiliar)); + NuiSetBindWatch (oPC, nToken, "chbx_familiar_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_familiar_event", JsonBool(TRUE)); + int nFamiliar = GetFamiliarCreatureType(oAssociate); + NuiSetBind(oPC, nToken, "cmb_familiar_selected", JsonInt(nFamiliar)); + string sFamiliarName = GetFamiliarName(oAssociate); + NuiSetBind(oPC, nToken, "txt_familiar_name", JsonString(sFamiliarName)); + if(!bIsPC) + { + NuiSetBind(oPC, nToken, "lbl_familiar_type_label", JsonString("Change familiar type")); + NuiSetBind(oPC, nToken, "lbl_familiar_name_label", JsonString("Change familiar name")); + NuiSetBind(oPC, nToken, "cmb_familiar_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "cmb_familiar_selected", TRUE); + NuiSetBind(oPC, nToken, "txt_familiar_name_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "txt_familiar_name", TRUE); + NuiSetBind(oPC, nToken, "btn_familiar_name_label", JsonString("Save")); + } + else + { + NuiSetBind(oPC, nToken, "lbl_familiar_type_label", JsonString("Familiar type")); + NuiSetBind(oPC, nToken, "lbl_familiar_name_label", JsonString("Familiar name")); + } + } + // Row 15 & 16 + if(bCompanion) + { + NuiSetBind(oPC, nToken, "chbx_companion_check", JsonBool(bBtnCompanion)); + NuiSetBindWatch (oPC, nToken, "chbx_companion_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_companion_event", JsonBool(TRUE)); + int nCompanion = GetAnimalCompanionCreatureType(oAssociate); + NuiSetBind(oPC, nToken, "cmb_companion_selected", JsonInt(nCompanion)); + string sCompanionName = GetAnimalCompanionName(oAssociate); + NuiSetBind(oPC, nToken, "txt_companion_name", JsonString(sCompanionName)); + if(!bIsPC) + { + NuiSetBind(oPC, nToken, "lbl_companion_type_label", JsonString("Change Companion type")); + NuiSetBind(oPC, nToken, "lbl_companion_name_label", JsonString("Change Companion name")); + NuiSetBind(oPC, nToken, "cmb_companion_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "cmb_companion_selected", TRUE); + NuiSetBind(oPC, nToken, "txt_companion_name_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "txt_companion_name", TRUE); + NuiSetBind(oPC, nToken, "btn_companion_name_label", JsonString("Save")); + } + else + { + NuiSetBind(oPC, nToken, "lbl_companion_type_label", JsonString("Companion type")); + NuiSetBind(oPC, nToken, "lbl_companion_name_label", JsonString("Companion name")); + } + } + if(bIsPC) + { + // Row 17+ + int nIndex, bWidget; + string sButton, sText; + json jPlugin = JsonArrayGet(jPCPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_event", JsonBool(TRUE)); + bWidget = JsonGetInt(JsonArrayGet(jPlugin, 1)); + if(bWidget < 3) + { + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_check", JsonBool(bWidget)); + NuiSetBindWatch (oPC, nToken, "chbx_plugin_" + sButton + "_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_event", JsonBool(TRUE)); + } + sText = " " + JsonGetString(JsonArrayGet(jPlugin, 2)); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_tooltip", JsonString(sText)); + jPlugin = JsonArrayGet(jPCPlugins, ++nIndex); + } + NuiSetBind(oPC, nToken, "chbx_plugin_tooltip", JsonString(" Adds the plugin to your widget.")); + } + // Row 18 + sText = ai_GetRandomTip(); + NuiSetBind(oPC, nToken, "lbl_info_1_label", JsonString(sText)); +} +void ai_CreateAssociateAINUI(object oPC, object oAssociate) +{ + // Set window to not save until it has been created. + 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); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jRow = JsonArray(); + json jCol = JsonArray(); + // Row 1 ******************************************************************* 500 / 73 + // If all the AI buttons are blocked then don't load the menu. + if(bIsPC) + { + bRight = GetLocalInt(GetModule(), sDMWidgetAccessVarname) != 7340028; + if(!AI_SERVER || bRight) + { + // If all the Command buttons are blocked then don't load the menu. + if(bRight) + { + jRow = CreateButton(jRow, "Command Menu", "btn_command_menu", 200.0, 20.0, -1.0, "btn_command_menu_tooltip"); + jRow = CreateLabel(jRow, "", "blank_label_2", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(!AI_SERVER) + { + 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); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + } + // Row 2 ******************************************************************* 500 / 73 + bRight = !ai_GetDMAIAccessButton(BTN_AI_LOOT); + if(bRight || !bIsPC) + { + jRow = JsonArray(); + if(!bIsPC) + { + jRow = CreateButton(jRow, "Command Menu", "btn_command_menu", 200.0, 20.0, -1.0, "btn_command_menu_tooltip"); + jRow = CreateLabel(jRow, "", "blank_label_2", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bRight) + { + jRow = CreateButton(jRow, "Loot Filter", "btn_loot_filter", 200.0, 20.0); + jRow = CreateLabel(jRow, "", "blank_label_2", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 3 ******************************************************************* 500 / 101 + bRight = !ai_GetDMAIAccessButton(BTN_AI_FOR_PC); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_REDUCE_SPEECH); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Player AI On/Off", "btn_ai", 200.0, 20.0, -1.0, "btn_ai_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ai", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Reduce Speech", "btn_quiet", 200.0, 20.0, -1.0, "btn_quiet_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_quiet", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 4 ******************************************************************* 500 / 129 + bRight = !ai_GetDMAIAccessButton(BTN_AI_USE_RANGED); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_STOP_WEAPON_EQUIP); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Ranged Combat", "btn_ranged", 200.0, 20.0, -1.0, "btn_ranged_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ranged", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Equip Best Weapons", "btn_equip_weapon", 200.0, 20.0, -1.0, "btn_equip_weapon_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_equip_weapon", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 5 ******************************************************************* 500 / 157 + bRight = !ai_GetDMAIAccessButton(BTN_AI_USE_SEARCH); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_USE_STEALTH); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Search Mode", "btn_search", 200.0, 20.0, -1.0, "btn_search_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_search", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Stealth Mode", "btn_stealth", 200.0, 20.0, -1.0, "btn_stealth_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_stealth", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 6 ******************************************************************* 500 / 185 + bRight = !ai_GetDMAIAccessButton(BTN_AI_OPEN_DOORS); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_REMOVE_TRAPS); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Open Door Mode", "btn_open_door", 200.0, 20.0, -1.0, "btn_open_door_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_open_door", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Disarm Traps Mode", "btn_traps", 200.0, 20.0, -1.0, "btn_traps_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_traps", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 7 ******************************************************************* 500 / 213 + bRight = !ai_GetDMAIAccessButton(BTN_AI_PICK_LOCKS); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_BASH_LOCKS); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Pick Locks Mode", "btn_pick_locks", 200.0, 20.0, -1.0, "btn_pick_locks_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_pick_locks", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Bash Mode", "btn_bash_locks", 200.0, 20.0, -1.0, "btn_bash_locks_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_bash_locks", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 8 ******************************************************************* 500 / 241 + bRight = !ai_GetDMAIAccessButton(BTN_AI_MAGIC_LEVEL); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_NO_SPONTANEOUS); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Magic usage level", "btn_magic_level", 200.0, 20.0f, -1.0, "btn_magic_level_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_magic_level", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Cleric Spontaneous Casting", "btn_spontaneous", 200.0, 20.0, -1.0, "btn_spontaneous_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_spontaneous", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 9 ******************************************************************* 500 / 269 + bRight = !ai_GetDMAIAccessButton(BTN_AI_NO_MAGIC_USE); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_NO_MAGIC_ITEM_USE); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Use Magic", "btn_magic", 200.0, 20.0, -1.0, "btn_magic_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_magic", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Use Magic Items", "btn_magic_items", 200.0, 20.0, -1.0, "btn_magic_items_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_magic_items", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 10 ****************************************************************** 500 / 297 + bRight = !ai_GetDMAIAccessButton(BTN_AI_DEF_MAGIC_USE); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_OFF_MAGIC_USE); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Use Defensive Magic Only", "btn_def_magic", 200.0, 20.0, -1.0, "btn_def_magic_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_def_magic", 25.0, 20.0f); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Use Offensive Magic Only", "btn_off_magic", 200.0, 20.0, -1.0, "btn_off_magic_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_off_magic", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 11 ****************************************************************** 500 / 325 + bRight = !ai_GetDMAIAccessButton(BTN_AI_HEAL_OUT); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_HEAL_IN); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Heal % Out of Combat", "btn_heal_out", 200.0, 20.0, -1.0, "btn_heal_out_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_heal_out", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Heal % in Combat", "btn_heal_in", 200.0, 20.0, -1.0, "btn_heal_in_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_heal_in", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 12 ****************************************************************** 500 / 353 + bRight = !ai_GetDMAIAccessButton(BTN_AI_STOP_SELF_HEALING); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_STOP_PARTY_HEALING); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Self Healing", "btn_heals_onoff", 200.0, 20.0, -1.0, "btn_heals_onoff_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_heals_onoff", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Party Healing", "btn_healp_onoff", 200.0, 20.0, -1.0, "btn_healp_onoff_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_healp_onoff", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 13 ****************************************************************** 500 / 391 + bRight = !ai_GetDMAIAccessButton(BTN_AI_STOP_CURE_SPELLS); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_LOOT); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Cast Cure Spells", "btn_cure_onoff", 200.0, 20.0, -1.0, "btn_cure_onoff_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cure_onoff", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + 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); + } + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 14 ****************************************************************** 500 / --- + bRight = !ai_GetDMAIAccessButton(BTN_AI_IGNORE_ASSOCIATES); + bLeft = !ai_GetDMAIAccessButton(BTN_AI_IGNORE_TRAPS); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + jRow = CreateButton(jRow, "Ignore Associates", "btn_ignore_assoc", 200.0, 20.0, -1.0, "btn_ignore_assoc_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ignore_assoc", 25.0, 20.0); + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + jRow = CreateButton(jRow, "Ignore floor Traps", "btn_ignore_traps", 200.0, 20.0, -1.0, "btn_ignore_traps_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ignore_traps", 25.0, 20.0); + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 15 ****************************************************************** 500 / --- + bRight = !ai_GetDMAIAccessButton(BTN_AI_PERC_RANGE); + bLeft = FALSE; //!ai_GetDMAIAccessButton(BTN_AI_PERC_RANGE); + if(bRight || bLeft) + { + jRow = JsonArray(); + if(bRight) + { + if(GetAssociateType(oAssociate) == ASSOCIATE_TYPE_HENCHMAN) + { + jRow = CreateButton(jRow, "Perception Range", "btn_perc_range", 200.0, 20.0, -1.0, "btn_perc_range_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_perc_range", 25.0, 20.0); + } + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + if(bLeft) + { + } + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 16 ****************************************************************** 500 / --- + bRight = !ai_GetDMWAccessButton(BTN_CMD_AI_SCRIPT); + if(bRight) + { + jRow = JsonArray(); + jRow = CreateButton(jRow, "Set Current AI:", "btn_ai_script", 175.0f, 20.0f, -1.0, "btn_ai_script_tooltip"); + jRow = CreateTextEditBox(jRow, "sPlaceHolder", "txt_ai_script", 16, FALSE, 145.0f, 20.0f, "txt_ai_script_tooltip"); + jRow = CreateCombo(jRow, ai_CreateAIScriptJson(oPC), "cmb_ai_script", 146.0, 20.0); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + // Row 17 ****************************************************************** 500 / --- + jRow = JsonArray(); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateLabel(jRow, "", "lbl_info", 475.0, 20.0, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + // Get the window location to restore it from the database. + float fX, fY; + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + jLocations = JsonObjectGet(jLocations, sAssociateType + AI_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sText, sName = GetName(oAssociate); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_NUI, sName + " AI Menu", + fX, fY, 500.0, fHeight + 12.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Get which buttons are activated. + int bAI = ai_GetAIButton(oPC, BTN_AI_FOR_PC, oAssociate, sAssociateType); + int bReduceSpeech = ai_GetAIButton(oPC, BTN_AI_REDUCE_SPEECH, oAssociate, sAssociateType); + int bRanged = ai_GetAIButton(oPC, BTN_AI_USE_RANGED, oAssociate, sAssociateType); + int bEquipWeapons = ai_GetAIButton(oPC, BTN_AI_STOP_WEAPON_EQUIP, oAssociate, sAssociateType); + int bSearch = ai_GetAIButton(oPC, BTN_AI_USE_SEARCH, oAssociate, sAssociateType); + int bStealth = ai_GetAIButton(oPC, BTN_AI_USE_STEALTH, oAssociate, sAssociateType); + int bOpenDoors = ai_GetAIButton(oPC, BTN_AI_OPEN_DOORS, oAssociate, sAssociateType); + int bTraps = ai_GetAIButton(oPC, BTN_AI_REMOVE_TRAPS, oAssociate, sAssociateType); + int bPickLocks = ai_GetAIButton(oPC, BTN_AI_PICK_LOCKS, oAssociate, sAssociateType); + int bBashLocks = ai_GetAIButton(oPC, BTN_AI_BASH_LOCKS, oAssociate, sAssociateType); + int bMagicLevel = ai_GetAIButton(oPC, BTN_AI_MAGIC_LEVEL, oAssociate, sAssociateType); + int bSpontaneous = ai_GetAIButton(oPC, BTN_AI_NO_SPONTANEOUS, oAssociate, sAssociateType); + int bNoMagic = ai_GetAIButton(oPC, BTN_AI_NO_MAGIC_USE, oAssociate, sAssociateType); + int bNoMagicItems = ai_GetAIButton(oPC, BTN_AI_NO_MAGIC_ITEM_USE, oAssociate, sAssociateType); + int bDefMagic = ai_GetAIButton(oPC, BTN_AI_DEF_MAGIC_USE, oAssociate, sAssociateType); + int bOffMagic = ai_GetAIButton(oPC, BTN_AI_OFF_MAGIC_USE, oAssociate, sAssociateType); + int bHealOut = ai_GetAIButton(oPC, BTN_AI_HEAL_OUT, oAssociate, sAssociateType); + int bHealIn = ai_GetAIButton(oPC, BTN_AI_HEAL_IN, oAssociate, sAssociateType); + int bSelfHealOnOff = ai_GetAIButton(oPC, BTN_AI_STOP_SELF_HEALING, oAssociate, sAssociateType); + int bPartyHealOnOff = ai_GetAIButton(oPC, BTN_AI_STOP_PARTY_HEALING, oAssociate, sAssociateType); + int bCureOnOff = ai_GetAIButton(oPC, BTN_AI_STOP_CURE_SPELLS, oAssociate, sAssociateType); + int bIgnoreAssociates = ai_GetAIButton(oPC, BTN_AI_IGNORE_ASSOCIATES, oAssociate, sAssociateType); + int bIgnoreTraps = ai_GetAIButton(oPC, BTN_AI_IGNORE_TRAPS, oAssociate, sAssociateType); + int bLoot = ai_GetAIButton(oPC, BTN_AI_LOOT, oAssociate, sAssociateType); + int bPercRange = ai_GetAIButton(oPC, BTN_AI_PERC_RANGE, oAssociate, sAssociateType); + // Save the associate to the nui for use in 0e_nui + json jData = JsonArray(); + jData = JsonArrayInsert(jData, JsonString(ObjectToString(oAssociate))); + NuiSetUserData(oPC, nToken, jData); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set all binds, events, and watches. + // Row 1 + // If all the AI buttons are blocked then don't load the menu. + if(bIsPC) + { + bRight = GetLocalInt(GetModule(), sDMWidgetAccessVarname) != 7340028; + if(!AI_SERVER || bRight) + { + // If all the Command buttons are blocked then don't load the menu. + if(bRight) + { + NuiSetBind(oPC, nToken, "btn_command_menu_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_command_menu_tooltip", JsonString(" " + sName + " Command options")); + } + if(!AI_SERVER) + { + NuiSetBind(oPC, nToken, "btn_main_menu_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_main_menu_tooltip", JsonString(" Module Options")); + } + fHeight += 28.0; + } + } + // Row 2 + if(!bIsPC) + { + NuiSetBind(oPC, nToken, "btn_command_menu_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_command_menu_tooltip", JsonString(" " + sName + " Command options")); + } + NuiSetBind(oPC, nToken, "btn_loot_filter_event", JsonBool(TRUE)); + 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) == "") + { + NuiSetBind(oPC, nToken, "chbx_ai_check", JsonBool(bAI)); + NuiSetBindWatch (oPC, nToken, "chbx_ai_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ai_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ai_event", JsonBool(TRUE)); + if(GetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT) == "xx_pc_1_hb") sText = " AI On"; + else sText = " AI Off"; + NuiSetBind(oPC, nToken, "btn_ai_tooltip", JsonString(sText)); + } + NuiSetBind(oPC, nToken, "chbx_quiet_check", JsonBool(bReduceSpeech)); + NuiSetBindWatch (oPC, nToken, "chbx_quiet_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_quiet_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_quiet_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK)) sText = " Reduced Speech On"; + else sText = " Reduces Speech Off"; + NuiSetBind (oPC, nToken, "btn_quiet_tooltip", JsonString(sText)); + // Row 4 + NuiSetBind(oPC, nToken, "chbx_ranged_check", JsonBool(bRanged)); + NuiSetBindWatch(oPC, nToken, "chbx_ranged_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ranged_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ranged_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_STOP_RANGED)) sText = " Ranged Off"; + else sText = " Ranged On"; + NuiSetBind (oPC, nToken, "btn_ranged_tooltip", JsonString(sText)); + NuiSetBind(oPC, nToken, "chbx_equip_weapon_check", JsonBool(bEquipWeapons)); + NuiSetBindWatch(oPC, nToken, "chbx_equip_weapon_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_equip_weapon_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_equip_weapon_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF)) sText = " Equiping Best Weapons Off"; + else sText = " Equiping Best Weapons On"; + NuiSetBind (oPC, nToken, "btn_equip_weapon_tooltip", JsonString(sText)); + // Row 5 + if(GetRacialType(oAssociate) != RACIAL_TYPE_ELF) + { + NuiSetBind(oPC, nToken, "chbx_search_check", JsonBool(bSearch)); + NuiSetBindWatch (oPC, nToken, "chbx_search_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_search_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_search_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH)) sText = " Search mode On"; + else sText = " Search mode Off"; + NuiSetBind (oPC, nToken, "btn_search_tooltip", JsonString(sText)); + } + NuiSetBind(oPC, nToken, "chbx_stealth_check", JsonBool(bStealth)); + NuiSetBindWatch(oPC, nToken, "chbx_stealth_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_stealth_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_stealth_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH)) sText = " Stealth mode On"; + else sText = " Stealth mode Off"; + NuiSetBind (oPC, nToken, "btn_stealth_tooltip", JsonString(sText)); + // Row 6 + string sRange = FloatToString(GetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE), 0, 0); + NuiSetBind(oPC, nToken, "chbx_open_door_check", JsonBool(bOpenDoors)); + NuiSetBindWatch (oPC, nToken, "chbx_open_door_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_open_door_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_open_door_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_OPEN_DOORS)) sText = " Open Doors On [" + sRange + " meters]"; + else sText = " Open Doors Off [" + sRange + " meters]"; + NuiSetBind (oPC, nToken, "btn_open_door_tooltip", JsonString(sText)); + sRange = FloatToString(GetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE), 0, 0); + 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)); + if(ai_GetAIMode(oAssociate, AI_MODE_DISARM_TRAPS)) sText = " Disable Traps On [" + sRange + " meters]"; + else sText = " Disable Traps Off [" + sRange + " meters]"; + NuiSetBind (oPC, nToken, "btn_traps_tooltip", JsonString(sText)); + // Row 7 + sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE), 0, 0); + 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)); + NuiSetBind(oPC, nToken, "btn_pick_locks_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_PICK_LOCKS)) sText = " Pick locks On [" + sRange + " meters]"; + else sText = " Pick Locks Off [" + sRange + " meters]"; + NuiSetBind (oPC, nToken, "btn_pick_locks_tooltip", JsonString(sText)); + NuiSetBind(oPC, nToken, "chbx_bash_locks_check", JsonBool(bBashLocks)); + NuiSetBindWatch(oPC, nToken, "chbx_bash_locks_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_bash_locks_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_bash_locks_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS)) sText = " Bash On [" + sRange + " meters]"; + else sText = " Bash Off [" + sRange + " meters]"; + NuiSetBind (oPC, nToken, "btn_bash_locks_tooltip", JsonString(sText)); + // Row 8 + string sMagic = IntToString(GetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT)); + NuiSetBind(oPC, nToken, "chbx_magic_level_check", JsonBool(bMagicLevel)); + NuiSetBindWatch (oPC, nToken, "chbx_magic_level_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_magic_level_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_level_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_magic_level_tooltip", JsonString(" Magic level [" + sMagic + "]")); + sText = " Spontaneous casting On"; + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_SPONTANEOUS_CURE)) sText = " Spontaneous casting Off"; + NuiSetBind(oPC, nToken, "chbx_spontaneous_check", JsonBool(bSpontaneous)); + NuiSetBindWatch (oPC, nToken, "chbx_spontaneous_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_spontaneous_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_spontaneous_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_spontaneous_tooltip", JsonString(sText)); + // Row 9 + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC)) sText = " Magic Off"; + else sText = " Magic On"; + NuiSetBind(oPC, nToken, "chbx_magic_check", JsonBool(bNoMagic)); + NuiSetBindWatch (oPC, nToken, "chbx_magic_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_tooltip", JsonString(sText)); + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS)) sText = " Magic Items Off"; + else sText = " Magic Items On"; + NuiSetBind(oPC, nToken, "chbx_magic_items_check", JsonBool(bNoMagicItems)); + NuiSetBindWatch (oPC, nToken, "chbx_magic_items_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_magic_items_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_items_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_items_tooltip", JsonString(sText)); + // Row 10 + if(ai_GetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING)) sText = " Defensive Magic On"; + else sText = " Defensive Magic Off"; + NuiSetBind(oPC, nToken, "chbx_def_magic_check", JsonBool (bDefMagic)); + NuiSetBindWatch (oPC, nToken, "chbx_def_magic_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_def_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_def_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_def_magic_tooltip", JsonString(sText)); + if(ai_GetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING)) sText = " Offensive Magic On"; + else sText = " Offensive Magic Off"; + NuiSetBind(oPC, nToken, "chbx_off_magic_check", JsonBool(bOffMagic)); + NuiSetBindWatch (oPC, nToken, "chbx_off_magic_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_off_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_off_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_off_magic_tooltip", JsonString(sText)); + // Row 11 + int nHeal = GetLocalInt(oAssociate, AI_HEAL_OUT_OF_COMBAT_LIMIT); + NuiSetBind(oPC, nToken, "chbx_heal_out_check", JsonBool(bHealOut)); + NuiSetBindWatch (oPC, nToken, "chbx_heal_out_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_heal_out_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_heal_out_event", JsonBool(TRUE)); + sText = " Will heal at or below [" + IntToString(nHeal) + "%] health out of combat"; + NuiSetBind(oPC, nToken, "btn_heal_out_tooltip", JsonString(sText)); + nHeal = GetLocalInt(oAssociate, AI_HEAL_IN_COMBAT_LIMIT); + 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)); + sText = " Will heal at or below [" + IntToString(nHeal) + "%] health in combat"; + NuiSetBind(oPC, nToken, "btn_heal_in_tooltip", JsonString(sText)); + // Row 12 + 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)); + NuiSetBind(oPC, nToken, "btn_heals_onoff_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF)) sText = " Self healing Off"; + else sText = " Self healing On"; + NuiSetBind(oPC, nToken, "btn_heals_onoff_tooltip", JsonString(sText)); + NuiSetBind(oPC, nToken, "chbx_healp_onoff_check", JsonBool(bPartyHealOnOff)); + NuiSetBind(oPC, nToken, "chbx_healp_onoff_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "chbx_healp_onoff_check", TRUE); + NuiSetBind(oPC, nToken, "btn_healp_onoff_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF)) sText = " Party healing Off"; + else sText = " Party healing On"; + NuiSetBind(oPC, nToken, "btn_healp_onoff_tooltip", JsonString(sText)); + // Row 13 + NuiSetBind(oPC, nToken, "btn_cure_onoff_tooltip", JsonString(sText)); + NuiSetBind(oPC, nToken, "chbx_cure_onoff_check", JsonBool(bCureOnOff)); + NuiSetBind(oPC, nToken, "chbx_cure_onoff_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "chbx_cure_onoff_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cure_onoff_event", JsonBool(TRUE)); + 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(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]"; + else sText = " Looting Off [" + sRange + " meters]"; + NuiSetBind(oPC, nToken, "chbx_loot_check", JsonBool(bLoot)); + NuiSetBindWatch (oPC, nToken, "chbx_loot_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_loot_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_loot_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_loot_tooltip", JsonString(sText)); + } + // Row 14 + NuiSetBind(oPC, nToken, "chbx_ignore_assoc_check", JsonBool(bIgnoreAssociates)); + NuiSetBindWatch(oPC, nToken, "chbx_ignore_assoc_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ignore_assoc_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ignore_assoc_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES)) sText = " Ignore Enemy Associates On"; + else sText = " Ignore Enemy Associates Off"; + NuiSetBind (oPC, nToken, "btn_ignore_assoc_tooltip", JsonString(sText)); + NuiSetBind(oPC, nToken, "chbx_ignore_traps_check", JsonBool(bIgnoreTraps)); + NuiSetBindWatch(oPC, nToken, "chbx_ignore_traps_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ignore_traps_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ignore_traps_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS)) sText = " Ignore Floor Traps On"; + else sText = " Ignore Floor Traps Off"; + NuiSetBind (oPC, nToken, "btn_ignore_traps_tooltip", JsonString(sText)); + // Row 15 + if(!bIsPC) + { + int nRange = GetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION + "_MENU"); + if(nRange < 8 || nRange > 11) + { + nRange = GetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION); + SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION + "_MENU", nRange); + } + if(nRange == 8) sText = " Perception Range Short [10 meters Sight / 10 meters Listen]"; + else if(nRange == 9) sText = " Perception Range Medium [20 meters Sight / 20 meters Listen]"; + else if(nRange == 10) sText = " Perception Range Long [35 meters Sight / 20 meters Listen]"; + else sText = " Perception Range Default [20 meters Sight / 20 meters Listen]"; + NuiSetBind(oPC, nToken, "chbx_perc_range_check", JsonBool(bPercRange)); + NuiSetBindWatch (oPC, nToken, "chbx_perc_range_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_perc_range_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_perc_range_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_perc_range_tooltip", JsonString(sText)); + } + // Row 16 + string sScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + if(sScript == "") sScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + NuiSetBind(oPC, nToken, "btn_ai_script_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ai_script_tooltip", JsonString(" Sets " + GetName(oAssociate) + " to use the ai script in the text box.")); + NuiSetBind(oPC, nToken, "txt_ai_script_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_ai_script", JsonString(sScript)); + NuiSetBind(oPC, nToken, "txt_ai_script_tooltip", JsonString(" Associate AI scripts must start with ai_a_")); + NuiSetBind(oPC, nToken, "cmb_ai_script_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "cmb_ai_script_selected", TRUE); + // Row 17 + sText = ai_GetRandomTip(); + NuiSetBind (oPC, nToken, "lbl_info_label", JsonString(sText)); +} +void ai_SetWidgetBinds(object oPC, object oAssociate, string sAssociateType, int nToken, string sName) +{ + int bBool, bIsPC = ai_GetIsCharacter(oAssociate); + string sText, sRange, sHeal; + // 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")); + NuiSetBind(oPC, nToken, "btn_open_main_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_open_main_tooltip", JsonString(" " + sName + " widget menu")); + if(bIsPC) sText = " All associates"; + else sText = " " + GetName(oAssociate); + if(ai_GetWidgetButton(oPC, BTN_CMD_ACTION, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_action_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_action_tooltip", JsonString(sText + " do actions")); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_GUARD, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_guard_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_guard_tooltip", JsonString(sText + " enter guard mode")); + bBool = ai_GetAIMode(oAssociate, AI_MODE_DEFEND_MASTER); + NuiSetBind(oPC, nToken, "btn_cmd_guard_encouraged", JsonBool(bBool)); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_HOLD, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_hold_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_hold_tooltip", JsonString(sText + " enter hold mode")); + bBool = ai_GetAIMode(oAssociate, AI_MODE_STAND_GROUND); + NuiSetBind(oPC, nToken, "btn_cmd_hold_encouraged", JsonBool(bBool)); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_ATTACK, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_attack_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_attack_tooltip", JsonString(sText + " enter normal mode")); + if(!bIsPC) + { + if(!ai_GetAIMode(oAssociate, AI_MODE_DEFEND_MASTER) && + !ai_GetAIMode(oAssociate, AI_MODE_STAND_GROUND) && + !ai_GetAIMode(oAssociate, AI_MODE_FOLLOW)) bBool = TRUE; + else bBool = FALSE; + if(!bIsPC) NuiSetBind(oPC, nToken, "btn_cmd_attack_encouraged", JsonBool(bBool)); + } + } + if(ai_GetWidgetButton(oPC, BTN_CMD_FOLLOW, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_follow_event", JsonBool(TRUE)); + float fRange = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE) + + StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oAssociate))); + string sRange = FloatToString(fRange, 0, 0); + if(bIsPC) + { + sText = " All associates"; + NuiSetBind(oPC, nToken, "btn_cmd_follow_tooltip", JsonString(sText + " enter follow mode")); + } + else + { + sText = " " + GetName(oAssociate); + NuiSetBind(oPC, nToken, "btn_cmd_follow_tooltip", JsonString(sText + " enter follow mode [" + sRange + " meters]")); + } + bBool = ai_GetAIMode(oAssociate, AI_MODE_FOLLOW); + if(!bIsPC) NuiSetBind(oPC, nToken, "btn_cmd_follow_encouraged", JsonBool(bBool)); + } + if(ai_GetAIButton(oPC, BTN_AI_FOLLOW_TARGET, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_follow_target_event", JsonBool(TRUE)); + object oTarget = GetLocalObject(oAssociate, AI_FOLLOW_TARGET); + string sTarget; + if(oTarget != OBJECT_INVALID) sTarget = GetName(oTarget); + else + { + if(ai_GetIsCharacter(oAssociate)) sTarget = "nobody"; + else sTarget = GetName(oPC); + } + float fRange = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE) + + StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oAssociate))); + string sRange = FloatToString(fRange, 0, 0); + NuiSetBind(oPC, nToken, "btn_follow_target_tooltip", JsonString(" " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]")); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_SEARCH, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_search_event", JsonBool(TRUE)); + if(ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_SEARCH)) sText = " leave "; + else sText = " enter "; + NuiSetBind(oPC, nToken, "btn_cmd_search_tooltip", JsonString(" Everyone" + sText + "search mode")); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_STEALTH, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_stealth_event", JsonBool(TRUE)); + if(ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_STEALTH)) sText = " leave "; + else sText = " enter "; + NuiSetBind(oPC, nToken, "btn_cmd_stealth_tooltip", JsonString(" Everyone" + sText + "stealth mode")); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_AI_SCRIPT, oAssociate, sAssociateType)) + { + sText = " Default tactics: Using the creatures base AI script"; + string sIcon = "ir_scommand"; + if(ResManGetAliasFor("0e_ch_1_hb", RESTYPE_NCS) != "") + { + string sScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT); + if(sScript == "ai_a_ambusher") + { + sText = " Ambusher: Attacks from a hidden position"; + sIcon = "ir_rogue"; + } + else if(sScript == "ai_a_flanker") + { + sText = " Flanker: Attacks enemies engaged with allies"; + sIcon = "ir_invite"; + } + else if(sScript == "ai_a_peaceful") + { + sText = " Peaceful: Avoids attacking any enemies if possible"; + sIcon = "ir_ignore"; + } + else if(sScript == "ai_a_defensive") + { + sText = " Defensive: Attacks then uses Expertise/Parry"; + sIcon = "ir_knockdwn"; + } + else if(sScript == "ai_a_ranged") + { + sText = " Ranged: Attacks from range as much as possible"; + sIcon = "ir_ranger"; + } + else if(sScript == "ai_a_cntrspell") + { + sText = " Counter Spell: Tries to counter enemy spells"; + sIcon = "ir_dcaster"; + } + } + else + { + if(GetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, oAssociate)) sText = "Using ambush tactics"; + if(GetCombatCondition(X0_COMBAT_FLAG_COWARDLY, oAssociate)) sText = "Using coward tactics"; + if(GetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, oAssociate)) sText = "Using defensive tactics"; + if(GetCombatCondition(X0_COMBAT_FLAG_RANGED, oAssociate)) sText = "Using ranged tactics"; + } + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_image", JsonString(sIcon)); + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_tooltip", JsonString(sText)); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_PLACE_TRAP, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cmd_place_trap_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_place_trap_tooltip", JsonString(" Place a trap at the location selected")); + } + 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")); + } + 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")); + } + 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")); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_JUMP_TO, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_jump_to_event", JsonBool(TRUE)); + sText = GetName(oPC); + if(oPC == oAssociate) sName = "everyone"; + else sName = GetName(oAssociate); + NuiSetBind(oPC, nToken, "btn_jump_to_tooltip", JsonString(" Jump " + sName + " to " + sText)); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_GHOST_MODE, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_ghost_mode_event", JsonBool (TRUE)); + sText = "On"; + 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))); + } + 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)); + NuiSetBind(oPC, nToken, "btn_inventory_tooltip", JsonString(" Open " + sName + " inventory")); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_FAMILIAR, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_familiar_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_familiar_tooltip", JsonString(" Summon " + sName + " familiar.")); + } + if(ai_GetWidgetButton(oPC, BTN_CMD_COMPANION, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_companion_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_companion_tooltip", JsonString(" Open " + sName + " Animal Companion.")); + } + if(ai_GetWidgetButton(oPC, BTN_BUFF_REST, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_buff_rest_event", JsonBool(TRUE)); + if(ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST)) sText = " Turn buffing after resting off"; + else sText = " Turn buffing after resting on."; + NuiSetBind(oPC, nToken, "btn_buff_rest_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_FOR_PC, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_ai_event", JsonBool(TRUE)); + if(GetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT) == "xx_pc_1_hb") sText = " AI [On] Turn off"; + else sText = " AI [Off] Turn on"; + NuiSetBind(oPC, nToken, "btn_ai_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_REDUCE_SPEECH, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_quiet_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK)) sText = " Reduced Speech On"; + else sText = " Reduced Speech Off"; + NuiSetBind(oPC, nToken, "btn_quiet_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_USE_RANGED, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_ranged_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_STOP_RANGED)) sText = " Ranged Off"; + else sText = " Ranged On"; + NuiSetBind(oPC, nToken, "btn_ranged_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_WEAPON_EQUIP, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_equip_weapon_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF)) sText = " Equiping Best Weapons Off"; + else sText = " Equiping Best Weapons On"; + NuiSetBind(oPC, nToken, "btn_equip_weapon_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_USE_SEARCH, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_search_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH)) sText = " Search On"; + else sText = " Search Off"; + NuiSetBind(oPC, nToken, "btn_search_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_USE_STEALTH, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_stealth_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH)) sText = " Stealth On"; + else sText = " Stealth Off"; + NuiSetBind(oPC, nToken, "btn_stealth_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_OPEN_DOORS, oAssociate, sAssociateType)) + { + sRange = FloatToString(GetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE), 0, 0); + NuiSetBind(oPC, nToken, "btn_open_door_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_OPEN_DOORS)) sText = " Open Doors On [" + sRange + " meters]"; + else sText = " Open Doors Off [" + sRange + " meters]"; + NuiSetBind(oPC, nToken, "btn_open_door_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_REMOVE_TRAPS, oAssociate, sAssociateType)) + { + sRange = FloatToString(GetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE), 0, 0); + NuiSetBind(oPC, nToken, "btn_traps_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_DISARM_TRAPS)) sText = " Disable Traps On [" + sRange + " meters]"; + else sText = " Disable Traps Off [" + sRange + " meters]"; + NuiSetBind(oPC, nToken, "btn_traps_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_PICK_LOCKS, oAssociate, sAssociateType)) + { + sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE), 0, 0); + NuiSetBind(oPC, nToken, "btn_pick_locks_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_PICK_LOCKS)) sText = " Pick locks On [" + sRange + " meters]"; + else sText = " Pick Locks Off [" + sRange + " meters]"; + NuiSetBind(oPC, nToken, "btn_pick_locks_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_BASH_LOCKS, oAssociate, sAssociateType)) + { + sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE), 0, 0); + NuiSetBind(oPC, nToken, "btn_bash_locks_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS)) sText = " Bash On [" + sRange + " meters]"; + else sText = " Bash Off [" + sRange + " meters]"; + NuiSetBind(oPC, nToken, "btn_bash_locks_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_MAGIC_LEVEL, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_magic_level_event", JsonBool(TRUE)); + string sMagic = IntToString(GetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT)); + NuiSetBind(oPC, nToken, "btn_magic_level_tooltip", JsonString(" Magic Level [" + sMagic + "]")); + } + if(ai_GetAIButton(oPC, BTN_AI_NO_SPONTANEOUS, oAssociate, sAssociateType)) + { + string sCasting = " Spontaneous casting On"; + if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_SPONTANEOUS_CURE)) sCasting = " Spontaneous casting Off"; + NuiSetBind(oPC, nToken, "btn_spontaneous_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_spontaneous_tooltip", JsonString(sCasting)); + } + if(ai_GetAIButton(oPC, BTN_AI_NO_MAGIC_USE, oAssociate, sAssociateType)) + { + if(ai_GetAIMode(oAssociate, AI_MAGIC_NO_MAGIC)) sText = " Magic Off"; + else sText = " Magic On"; + NuiSetBind(oPC, nToken, "btn_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_NO_MAGIC_ITEM_USE, oAssociate, sAssociateType)) + { + if(ai_GetAIMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS)) sText = " Magic Items Off"; + else sText = " Magic Items On"; + NuiSetBind(oPC, nToken, "btn_magic_items_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_items_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_DEF_MAGIC_USE, oAssociate, sAssociateType)) + { + if(ai_GetAIMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING)) sText = " Defensive Magic On"; + else sText = " Defensive Magic Off"; + NuiSetBind(oPC, nToken, "btn_def_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_def_magic_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_OFF_MAGIC_USE, oAssociate, sAssociateType)) + { + if(ai_GetAIMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING)) sText = " Offensive Magic On"; + else sText = " Offensive Magic Off"; + NuiSetBind(oPC, nToken, "btn_off_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_off_magic_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_HEAL_OUT, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_heal_out_event", JsonBool(TRUE)); + sHeal = IntToString(GetLocalInt(oAssociate, AI_HEAL_OUT_OF_COMBAT_LIMIT)); + sText = " Will heal at or below [" + sHeal + "%] health out of combat"; + NuiSetBind(oPC, nToken, "btn_heal_out_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_HEAL_IN, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_heal_in_event", JsonBool(TRUE)); + sHeal = IntToString(GetLocalInt(oAssociate, AI_HEAL_IN_COMBAT_LIMIT)); + sText = " Will heal at or below [" + sHeal + "%] health in combat"; + NuiSetBind(oPC, nToken, "btn_heal_in_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_SELF_HEALING, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_heals_onoff_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF)) sText = " Self healing Off"; + else sText = " Self healing On"; + NuiSetBind(oPC, nToken, "btn_heals_onoff_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_PARTY_HEALING, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_healp_onoff_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF)) sText = " Party healing Off"; + else sText = " Party healing On"; + NuiSetBind(oPC, nToken, "btn_healp_onoff_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_CURE_SPELLS, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_cure_onoff_event", JsonBool(TRUE)); + 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(ai_GetAIButton(oPC, BTN_AI_LOOT, oAssociate, sAssociateType)) + { + sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE), 0, 0); + string sLoot = " Looting Off [" + sRange + " meters]"; + if(ai_GetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS)) sLoot = " Looting On [" + sRange + " meters]"; + NuiSetBind(oPC, nToken, "btn_loot_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_loot_tooltip", JsonString(sLoot)); + } + if(ai_GetAIButton(oPC, BTN_AI_IGNORE_ASSOCIATES, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_ignore_assoc_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES)) sText = " Ignore Enemy Associates On"; + else sText = " Ignore Enemy Associates Off"; + NuiSetBind(oPC, nToken, "btn_ignore_assoc_tooltip", JsonString(sText)); + } + if(ai_GetAIButton(oPC, BTN_AI_IGNORE_TRAPS, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_ignore_traps_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS)) sText = " Ignore Floor Traps On"; + else sText = " Ignore Floor Traps Off"; + NuiSetBind(oPC, nToken, "btn_ignore_traps_tooltip", JsonString(sText)); + } + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + if(ai_GetAIButton(oPC, BTN_AI_PERC_RANGE, oAssociate, sAssociateType)) + { + int nRange = GetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION); + if(nRange < 8 || nRange > 11) + { + nRange = 11; + SetLocalInt(oAssociate, AI_ASSOCIATE_PERCEPTION, 11); + jAIData = JsonArraySet(jAIData, 7, JsonInt(11)); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + if(nRange == 8) sText = " Perception Range Short [10 meters Sight / 10 meters Listen]"; + if(nRange == 9) sText = " Perception Range Medium [20 meters Sight / 20 meters Listen]"; + if(nRange == 10) sText = " Perception Range Long [35 meters Sight / 20 meters Listen]"; + else sText = " Perception Range Default [20 meters Sight / 20 meters Listen]"; + NuiSetBind(oPC, nToken, "btn_perc_range_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_perc_range_tooltip", JsonString(sText)); + } + if(bIsPC) + { + int nIndex, bWidget; + string sButton, sName, sText, sScript; + json jPCPlugins = ai_UpdatePluginsForPC(oPC); + json jPlugin = JsonArrayGet(jPCPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + bWidget = JsonGetInt(JsonArrayGet(jPlugin, 1)); + if(bWidget) + { + sButton = IntToString(nIndex); + sScript = JsonGetString(JsonArrayGet(jPlugin, 0)); + if(ResManGetAliasFor(sScript, RESTYPE_NCS) == "") + { + sText = " " + sScript + " not found by ResMan!"; + } + else sName = " " + JsonGetString(JsonArrayGet(jPlugin, 2)); + NuiSetBind(oPC, nToken, "btn_exe_plugin_" + sButton + "_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_exe_plugin_" + sButton + "_tooltip", JsonString(sName)); + } + jPlugin = JsonArrayGet(jPCPlugins, ++nIndex); + } + } + if(ai_GetWidgetButton(oPC, BTN_CMD_SPELL_WIDGET, oAssociate, sAssociateType)) + { + NuiSetBind(oPC, nToken, "btn_update_widget_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_update_widget_tooltip", JsonString(" Updates Quick Use Widget")); + json jSpell, jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + object oItem; + if(JsonGetType(jWidget) != JSON_TYPE_NULL) + { + int nLevel, nSpell, nIndex, nClass, nMetaMagic, nDomain, nSubSpell, nFeat, nSAIndex; + string sSpellIcon, sMetaMagicText, sSubSpell, sClass, sIndex; + while(nIndex < 10) + { + jSpell = JsonArrayGet(jWidget, nIndex); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + sIndex = IntToString(nIndex); + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + 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 NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + else + { + nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); + if(nFeat) // This is a feat. + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + 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 + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + if(GetHasFeat(nFeat, oAssociate)) + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); + } + else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + else // This is a spell. + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + 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 + "_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)); + nSAIndex = JsonGetInt(JsonArrayGet(jSpell, 6)); + if(nClass == 255 && GetSpellAbilityReady(oAssociate, nSAIndex)) + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName + " (Special Ability / " + IntToString(nLevel) + ")")); + } + else if(GetSpellUsesLeft(oAssociate, nClass, nSpell, nMetaMagic, nDomain)) + { + 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 break; + ++nIndex; + } + while(nIndex < 20) + { + jSpell = JsonArrayGet(jWidget, nIndex); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + sIndex = IntToString(nIndex); + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); + if(nClass == -1) // This is an Item. + { + oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + if(oItem != OBJECT_INVALID) + { + 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)); + 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 NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + else jWidget = JsonArrayDel(jWidget, nIndex--); + } + else if(nFeat) // This is a feat. + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + 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 + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_image", JsonString(sSpellIcon)); + if(GetHasFeat(nFeat, oAssociate)) + { + NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_tooltip", JsonString(" " + sName)); + } + else NuiSetBind(oPC, nToken, "btn_widget_" + sIndex + "_event", JsonBool(FALSE)); + } + else // This is a spell. + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + 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) + + // " nDomain: " + IntToString(nDomain) + " nLevel: " + IntToString(nLevel)); + 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)); + sSubSpell = Get2DAString("spells", "Master", nSpell); + if(sSubSpell != "") nSpell = StringToInt(sSubSpell); + 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 if(GetSpellUsesLeft(oAssociate, nClass, nSpell, nMetaMagic, nDomain)) + { + 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 break; + ++nIndex; + } + } + } +} +void ai_CreateWidgetNUI(object oPC, object oAssociate) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0f, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + if(sAssociateType == "") return; + int bAIWidgetLock = ai_GetWidgetButton(oPC, BTN_WIDGET_LOCK, oAssociate, sAssociateType); + int bVertical = ai_GetWidgetButton(oPC, BTN_WIDGET_VERTICAL, oAssociate, sAssociateType); + float fButtons; + // ************************************************************************* Width / Height + // Row 1 (buttons)********************************************************** + // Setup the main associate button to use their portrait. + json jButton = NuiEnabled(NuiId (NuiButtonImage(NuiBind("btn_open_main_image")), "btn_open_main"), NuiBind("btn_open_main_event")); + jButton = NuiWidth(jButton, 35.0); + 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)); + json jRow = JsonArrayInsert(JsonArray(), jButton); + 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"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_GUARD, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_guard", "btn_cmd_guard", 35.0f, 35.0f, 0.0, "btn_cmd_guard_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_HOLD, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_standground", "btn_cmd_hold", 35.0f, 35.0f, 0.0, "btn_cmd_hold_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_ATTACK, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_attacknearest", "btn_cmd_attack", 35.0f, 35.0f, 0.0, "btn_cmd_attack_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_FOLLOW, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_follow", "btn_cmd_follow", 35.0f, 35.0f, 0.0, "btn_cmd_follow_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_FOLLOW_TARGET, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_dmchat", "btn_follow_target", 35.0f, 35.0f, 0.0, "btn_follow_target_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_SEARCH, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ife_foc_search", "btn_cmd_search", 35.0f, 35.0f, 0.0, "btn_cmd_search_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_STEALTH, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ife_foc_hide", "btn_cmd_stealth", 35.0f, 35.0f, 0.0, "btn_cmd_stealth_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_AI_SCRIPT, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "", "btn_cmd_ai_script", 35.0f, 35.0f, 0.0, "btn_cmd_ai_script_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_PLACE_TRAP, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "isk_settrap", "btn_cmd_place_trap", 35.0f, 35.0f, 0.0, "btn_cmd_place_trap_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_BUFF_SHORT, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_cantrips", "btn_buff_short", 35.0f, 35.0f, 0.0, "btn_buff_short_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_BUFF_LONG, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_cast", "btn_buff_long", 35.0f, 35.0f, 0.0, "btn_buff_long_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_BUFF_ALL, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_level789", "btn_buff_all", 35.0f, 35.0f, 0.0, "btn_buff_all_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_BUFF_REST, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_rest", "btn_buff_rest", 35.0f, 35.0f, 0.0, "btn_buff_rest_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_JUMP_TO, oAssociate, sAssociateType)) + { + string sImage; + if(oPC == oAssociate) sImage = "dm_jumpall"; + else sImage = "dm_jump"; + jRow = CreateButtonImage(jRow, sImage, "btn_jump_to", 35.0f, 35.0f, 0.0, "btn_jump_to_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_GHOST_MODE, oAssociate, sAssociateType)) + { + 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"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_FAMILIAR, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ife_familiar", "btn_familiar", 35.0f, 35.0f, 0.0, "btn_familiar_tooltip"); + fButtons += 1.0; + } + if(ai_GetWidgetButton(oPC, BTN_CMD_COMPANION, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ife_animal", "btn_companion", 35.0f, 35.0f, 0.0, "btn_companion_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_FOR_PC, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "dm_ai", "btn_ai", 35.0f, 35.0f, 0.0, "btn_ai_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_REDUCE_SPEECH, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "isk_movsilent", "btn_quiet", 35.0f, 35.0f, 0.0, "btn_quiet_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_USE_RANGED, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_archer", "btn_ranged", 35.0f, 35.0f, 0.0, "btn_ranged_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_WEAPON_EQUIP, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "dm_takeitem", "btn_equip_weapon", 35.0f, 35.0f, 0.0, "btn_equip_weapon_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_USE_SEARCH, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "isk_search", "btn_search", 35.0f, 35.0f, 0.0, "btn_search_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_USE_STEALTH, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "isk_hide", "btn_stealth", 35.0f, 35.0f, 0.0, "btn_stealth_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_OPEN_DOORS, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_open", "btn_open_door", 35.0f, 35.0f, 0.0, "btn_open_door_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_REMOVE_TRAPS, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "isk_distrap", "btn_traps", 35.0f, 35.0f, 0.0, "btn_traps_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_PICK_LOCKS, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "isk_olock", "btn_pick_locks", 35.0f, 35.0f, 0.0, "btn_pick_locks_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_BASH_LOCKS, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_bash", "btn_bash_locks", 35.0f, 35.0f, 0.0, "btn_bash_locks_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_MAGIC_LEVEL, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "dm_control", "btn_magic_level", 35.0f, 35.0f, 0.0, "btn_magic_level_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_NO_SPONTANEOUS, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_xability", "btn_spontaneous", 35.0f, 35.0f, 0.0, "btn_spontaneous_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_NO_MAGIC_USE, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_cntrspell", "btn_magic", 35.0f, 35.0f, 0.0, "btn_magic_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_NO_MAGIC_ITEM_USE, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_moreattacks", "btn_magic_items", 35.0f, 35.0f, 0.0, "btn_magic_items_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_DEF_MAGIC_USE, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_orisons", "btn_def_magic", 35.0f, 35.0f, 0.0, "btn_def_magic_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_OFF_MAGIC_USE, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_metamagic", "btn_off_magic", 35.0f, 35.0f, 0.0, "btn_off_magic_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_HEAL_OUT, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "isk_heal", "btn_heal_out", 35.0f, 35.0f, 0.0, "btn_heal_out_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_HEAL_IN, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "dm_heal", "btn_heal_in", 35.0f, 35.0f, 0.0, "btn_heal_in_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_SELF_HEALING, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_heal", "btn_heals_onoff", 35.0f, 35.0f, 0.0, "btn_heals_onoff_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_PARTY_HEALING, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_party", "btn_healp_onoff", 35.0f, 35.0f, 0.0, "btn_healp_onoff_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_STOP_CURE_SPELLS, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_accept", "btn_cure_onoff", 35.0f, 35.0f, 0.0, "btn_cure_onoff_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_LOOT, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_barter", "btn_loot", 35.0f, 35.0f, 0.0, "btn_loot_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_IGNORE_ASSOCIATES, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_ignore", "btn_ignore_assoc", 35.0f, 35.0f, 0.0, "btn_ignore_assoc_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_IGNORE_TRAPS, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_abort", "btn_ignore_traps", 35.0f, 35.0f, 0.0, "btn_ignore_traps_tooltip"); + fButtons += 1.0; + } + if(ai_GetAIButton(oPC, BTN_AI_PERC_RANGE, oAssociate, sAssociateType)) + { + jRow = CreateButtonImage(jRow, "ir_dmchat", "btn_perc_range", 35.0f, 35.0f, 0.0, "btn_perc_range_tooltip"); + fButtons += 1.0; + } + int bIsPC = ai_GetIsCharacter(oAssociate); + if(bIsPC) + { + json jPCPlugins = ai_UpdatePluginsForPC(oPC); + // Plug in buttons ***************************************************** + int nIndex, bWidget; + string sIcon, sButton; + json jPlugin = JsonArrayGet(jPCPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + bWidget = JsonGetInt(JsonArrayGet(jPlugin, 1)); + if(bWidget == 1) + { + sIcon = JsonGetString(JsonArrayGet(jPlugin, 3)); + sButton = IntToString(nIndex); + jRow = CreateButtonImage(jRow, sIcon, "btn_exe_plugin_" + sButton, 35.0f, 35.0f, 0.0, "btn_exe_plugin_" + sButton + "_tooltip"); + fButtons += 1.0; + } + jPlugin = JsonArrayGet(jPCPlugins, ++nIndex); + } + } + float fHeight, fWidth; + if(bAIWidgetLock) + { + fWidth = 50.0f; + fHeight = 50.0; + } + else if(bVertical) + { + fWidth = 88.0f; + fHeight = 55.0f; + } + else + { + fWidth = 55.0f; + fHeight = 88.0f; + } + // Quick Widget. + int nIndex, nSpell, nLevel, nMetaMagic; + float fQuickWidgetColumns; + string sClass, sLevel, sIndex; + object oItem; + json jSpell; + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + json jCol = JsonArray(); + if(ai_GetWidgetButton(oPC, BTN_CMD_SPELL_WIDGET, oAssociate, sAssociateType) && + JsonGetLength(jWidget) > 0) + { + // Row 2 (Widget Row 1)************************************************* + if(JsonGetType(jWidget) != JSON_TYPE_NULL) + { + fQuickWidgetColumns += 1.0; + int bAdd; + float fSpellButtons; + json jButton, jRectangle, jMetaMagic, jDrawList; + // Add row to the column. + if(bVertical) jCol = JsonArrayInsert(jCol, NuiCol(jRow)); + else jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + jRow = CreateButtonImage(JsonArray(), "ir_back", "btn_update_widget", 35.0f, 35.0f, 0.0, "btn_update_widget_tooltip"); + //CreateLabel(jRow, "", "blank_label", 35.0, 35.0, 0, 0, 0.0); + while(nIndex < 10) + { + bAdd = TRUE; + jSpell = JsonArrayGet(jWidget, nIndex); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + if(JsonGetInt(JsonArrayGet(jSpell, 1)) == -1) + { + oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + if(oItem == OBJECT_INVALID) + { + bAdd = FALSE; + jWidget = JsonArrayDel(jWidget, nIndex--); + jSpells = JsonArrayInsert(jSpells, jWidget, 2); + jAIData = JsonArrayInsert(jAIData, jSpells, 10); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + } + if(bAdd) + { + sIndex = IntToString(nIndex); + jButton = NuiButtonImage(NuiBind("btn_widget_" + sIndex + "_image")); + jButton = NuiEnabled(jButton, NuiBind("btn_widget_" + sIndex + "_event")); + jButton = NuiId(jButton, "btn_widget_" + sIndex); + 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); + jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); + jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + jRow = JsonArrayInsert(jRow, jButton); + fSpellButtons += 1.0; + } + } + else break; + ++nIndex; + } + if(fSpellButtons > fButtons) fButtons = fSpellButtons; + // Row 3 (Widget Row 2)************************************************* + if(nIndex > 9 && JsonGetLength(jWidget) > 10) + { + fQuickWidgetColumns += 1.0; + // Add row to the column. + if(bVertical) jCol = JsonArrayInsert(jCol, NuiCol(jRow)); + else jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + jRow = CreateLabel(JsonArray(), "", "blank_label", 35.0, 35.0, 0, 0, 0.0); + while(nIndex < 20) + { + jSpell = JsonArrayGet(jWidget, nIndex); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + if(JsonGetInt(JsonArrayGet(jSpell, 1)) == -1) + { + oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + if(oItem == OBJECT_INVALID) + { + bAdd = FALSE; + jWidget = JsonArrayDel(jWidget, nIndex--); + jSpells = JsonArrayInsert(jSpells, jWidget, 2); + jAIData = JsonArrayInsert(jAIData, jSpells, 10); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + } + if(bAdd) + { + sIndex = IntToString(nIndex); + jButton = NuiButtonImage(NuiBind("btn_widget_" + sIndex + "_image")); + jButton = NuiEnabled(jButton, NuiBind("btn_widget_" + sIndex + "_event")); + jButton = NuiId(jButton, "btn_widget_" + sIndex); + 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); + jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); + jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + jRow = JsonArrayInsert(jRow, jButton); + fSpellButtons += 1.0; + } + } + else break; + ++nIndex; + } + } + } + // Add the row to the column. + if(nIndex > 0) + { + if(bVertical) jCol = JsonArrayInsert(jCol, NuiCol(jRow)); + else jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } + } + else + { + // Add the row to the column. + if(bVertical) jCol = JsonArrayInsert(jCol, NuiCol(jRow)); + else jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + } + float fScale = GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_SCALE) / 100.0; + float fButtonScale; + // 1.1 = 2.5 2.0 = 6.0 Ranges we need for scales to work correctly. + if(fScale > 1.0) fButtonScale = (fScale - 1.1) / (2.0 - 1.1) * 3.5 + 2.5; + else fButtonScale = 1.0; + if(fButtons > 0.0f) + { + if(bVertical) fWidth = fWidth + fButtons * 35.0f + fButtons * fButtonScale; + else fWidth = fWidth + fButtons * 35.0f; + } + if(fQuickWidgetColumns > 0.0f) + { + if(bVertical) fHeight = fHeight + fQuickWidgetColumns * 39.0f; + else fHeight = fHeight + fQuickWidgetColumns * 39.0f + fQuickWidgetColumns * fButtonScale; + } + // Get the window location to restore it from the database. + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) + { + ai_SetupAssociateData(oPC, oAssociate, sAssociateType); + jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + } + 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(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; + string sAssociateName = GetName(oAssociate); + while(nIndex < AI_MAX_HENCHMAN) + { + if(sAssociateName == GetName(GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex))) + { + fY = (88.0 + 88.0 * IntToFloat(nIndex - 1)); + break; + } + nIndex++; + } + } + fY = fY * fScale; + } + if(bAIWidgetLock) + { + fX += 4.0f; + // GUI scales are a mess, I just figured them out per scale to keep the widget from moving. + if(fScale == 1.0) fY += 37.0; + else if(fScale == 1.1) fY += 38.0; + else if(fScale == 1.2) fY += 40.0; + else if(fScale == 1.3) fY += 42.0; + else if(fScale == 1.4) fY += 43.0; + else if(fScale == 1.5) fY += 45.0; + else if(fScale == 1.6) fY += 47.0; + else if(fScale == 1.7) fY += 48.0; + else if(fScale == 1.8) fY += 50.0; + else if(fScale == 1.9) fY += 52.0; + else if(fScale == 2.0) fY += 54.0; + } + // Set the layout of the window. + json jLayout; + int nToken, bBool; + string sHeal, sText, sRange; + string sName = GetName(oAssociate); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + if(bVertical) + { + jLayout = NuiRow(jCol); + if(bAIWidgetLock) nToken = SetWindow(oPC, jLayout, sAssociateType + AI_WIDGET_NUI, "AI Widget", fX, fY, fHeight, fWidth, FALSE, FALSE, FALSE, TRUE, FALSE, "0e_nui"); + else nToken = SetWindow(oPC, jLayout, sAssociateType + AI_WIDGET_NUI, sName + " Widget", fX, fY, fHeight, fWidth, FALSE, FALSE, FALSE, TRUE, TRUE, "0e_nui"); +} + else + { + jLayout = NuiCol(jCol); + if(bAIWidgetLock) nToken = SetWindow(oPC, jLayout, sAssociateType + AI_WIDGET_NUI, "AI Widget", fX, fY, fWidth, fHeight, FALSE, FALSE, FALSE, TRUE, FALSE, "0e_nui"); + else nToken = SetWindow(oPC, jLayout, sAssociateType + AI_WIDGET_NUI, sName + " Widget", fX, fY, fWidth, fHeight, FALSE, FALSE, FALSE, TRUE, TRUE, "0e_nui"); + } + // Save the associate to the nui. + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oAssociate))); + NuiSetUserData(oPC, nToken, jData); + ai_SetWidgetBinds(oPC, oAssociate, sAssociateType, nToken, sName); +} +json ai_CreateLootFilterRow(json jRow, string sLabel, int nIndex) +{ + string sIndex = IntToString(nIndex); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateTextEditBox(jRow, "plc_hold", "txt_gold_" + sIndex, 9, FALSE, 90.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateCheckBox(jRow, sLabel, "chbx_" + sIndex, 200.0, 20.0); + return JsonArrayInsert(jRow, NuiSpacer()); +} +void ai_SetupLootElements(object oPC, object oAssociate, int nToken, int nLootBit, int nIndex) +{ + string sIndex = IntToString(nIndex); + int bLoot = ai_GetLootFilter(oAssociate, nLootBit); + NuiSetBind(oPC, nToken, "chbx_" + sIndex + "_check", JsonBool(bLoot)); + NuiSetBindWatch (oPC, nToken, "chbx_" + sIndex + "_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_" + sIndex + "_event", JsonBool(TRUE)); + string sGold = IntToString(GetLocalInt(oAssociate, AI_MIN_GOLD_ + sIndex)); + NuiSetBind(oPC, nToken, "txt_gold_" + sIndex + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_gold_" + sIndex, JsonString(sGold)); + NuiSetBindWatch (oPC, nToken, "txt_gold_" + sIndex, TRUE); +} +void ai_CreateLootFilterNUI(object oPC, object oAssociate) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 318 / 73 + int bIsPC = ai_GetIsCharacter(oAssociate); + json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateCheckBox(jRow, "Give all loot to the player", "chbx_give_loot", 200.0, 20.0, "chbx_give_loot_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 *************************************************************** 388 / 101 + jRow = JsonArray(); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateTextEditBox(jRow, "plc_hold", "txt_max_weight", 9, FALSE, 50.0, 20.0, "txt_max_weight_tooltip"); + jRow = CreateLabel(jRow, "Maximum Weight to pickup", "lbl_weight", 200.0, 20.0, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 *************************************************************** 388 / 129 + jRow = JsonArray(); + jRow = CreateButton(jRow, "Set All", "btn_set_all", 110.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Clear All", "btn_clear_all", 110.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 *************************************************************** 388 / 157 + jRow = JsonArray(); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateLabel(jRow, "Minimum Gold", "lbl_min_gold", 100.0, 20.0, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateLabel(jRow, "Items to Pickup", "lbl_pickup", 140.0, 20.0, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 5 *************************************************************** 388 / 185 + jRow = ai_CreateLootFilterRow(JsonArray(), "Plot items", 2); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 6 *************************************************************** 388 / 213 + jRow = ai_CreateLootFilterRow(JsonArray(), "Armor", 3); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 7 *************************************************************** 388 / 241 + jRow = ai_CreateLootFilterRow(JsonArray(), "Belts", 4); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 8 *************************************************************** 388 / 269 + jRow = ai_CreateLootFilterRow(JsonArray(), "Boots", 5); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 9 *************************************************************** 388 / 297 + jRow = ai_CreateLootFilterRow(JsonArray(), "Cloaks", 6); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 10 *************************************************************** 388 / 325 + jRow = ai_CreateLootFilterRow(JsonArray(), "Gems", 7); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 11 *************************************************************** 388 / 353 + jRow = ai_CreateLootFilterRow(JsonArray(), "Gloves and Bracers", 8); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 12 *************************************************************** 388 / 381 + jRow = ai_CreateLootFilterRow(JsonArray(), "Headgear", 9); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 13 *************************************************************** 388 / 409 + jRow = ai_CreateLootFilterRow(JsonArray(), "Jewelry", 10); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 14 *************************************************************** 388 / 437 + jRow = ai_CreateLootFilterRow(JsonArray(), "Miscellaneous items", 11); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 15 *************************************************************** 388 / 465 + jRow = ai_CreateLootFilterRow(JsonArray(), "Potions", 12); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 16 *************************************************************** 388 / 493 + jRow = ai_CreateLootFilterRow(JsonArray(), "Scrolls", 13); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 17 *************************************************************** 388 / 521 + jRow = ai_CreateLootFilterRow(JsonArray(), "Shields", 14); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 18 *************************************************************** 388 / 549 + jRow = ai_CreateLootFilterRow(JsonArray(), "Wands, Rods, and Staves", 15); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 19 ************************************************************** 388 / 577 + jRow = ai_CreateLootFilterRow(JsonArray(), "Weapons", 16); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 20 ************************************************************** 388 / 605 + jRow = ai_CreateLootFilterRow(JsonArray(), "Arrows", 17); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 21 ************************************************************** 388 / 633 + jRow = ai_CreateLootFilterRow(JsonArray(), "Bolts", 18); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 22 ************************************************************** 388 / 661 + jRow = ai_CreateLootFilterRow(JsonArray(), "Bullets", 19); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + // Get the window location to restore it from the database. + float fX, fY; + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + jLocations = JsonObjectGet(jLocations, sAssociateType + AI_LOOTFILTER_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sText, sName = 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", + fX, fY, 318.0, 673.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Save the associate to the nui. + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oAssociate))); + NuiSetUserData(oPC, nToken, jData); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set all binds, events, and watches. + // Row 1 + int bGiveLoot = ai_GetLootFilter(oAssociate, AI_LOOT_GIVE_TO_PC); + NuiSetBind(oPC, nToken, "chbx_give_loot_check", JsonBool (bGiveLoot)); + NuiSetBindWatch (oPC, nToken, "chbx_give_loot_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_give_loot_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_give_loot_tooltip", JsonString( + " Check this to make henchman give any loot picked up to the player.")); + // Row 2 + int nWeight = GetLocalInt(oAssociate, AI_MAX_LOOT_WEIGHT); + if(nWeight == 0) + { + nWeight = 200; + SetLocalInt(oAssociate, AI_MAX_LOOT_WEIGHT, nWeight); + } + NuiSetBind(oPC, nToken, "txt_max_weight_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_max_weight", JsonString(IntToString(nWeight))); + NuiSetBindWatch (oPC, nToken, "txt_max_weight", TRUE); + NuiSetBind(oPC, nToken, "txt_max_weight_tooltip", JsonString(" Max weighted item you will pickup from 1 to 1,000")); + // Row 3 + NuiSetBind(oPC, nToken, "btn_set_all_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_set_all", JsonInt(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_all_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_all", JsonInt(TRUE)); + // Row 4 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_PLOT, 2); + // Row 5 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_ARMOR, 3); + // Row 6 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_BELTS, 4); + // Row 7 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_BOOTS, 5); + // Row 8 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_CLOAKS, 6); + // Row 9 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_GEMS, 7); + // Row 10 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_GLOVES, 8); + // Row 11 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_HEADGEAR, 9); + // Row 12 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_JEWELRY, 10); + // Row 13 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_MISC, 11); + // Row 14 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_POTIONS, 12); + // Row 15 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_SCROLLS, 13); + // Row 16 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_SHIELDS, 14); + // Row 17 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_WANDS_RODS_STAVES, 15); + // Row 18 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_WEAPONS, 16); + // Row 19 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_ARROWS, 17); + // Row 20 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_BOLTS, 18); + // Row 21 + ai_SetupLootElements(oPC, oAssociate, nToken, AI_LOOT_BULLETS, 19); +} +void ai_CreateCopySettingsNUI(object oPC, object oAssociate) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 244 / 73 + string sName = GetName(oAssociate); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, "Copy settings to", "lbl_paste", 220.0, 20.0, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 ******************************************************************* 244 / 101 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "All Associates", "btn_paste_all", 220.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 2 ******************************************************************* 244 / 129 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Familiar", "btn_paste_familiar", 220.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 ******************************************************************* 244 / 157 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Companion", "btn_paste_companion", 220.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // 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 / 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) + { + sAssocName = GetName(oAssoc); + if(GetStringRight(sAssocName, 1) == "s") sAssocName = sAssocName + "'"; + else sAssocName = sAssocName + "'s"; + jRow = CreateButton(JsonArray(), sAssocName, "btn_paste_henchman" + IntToString(nIndex), 220.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + else break; + } + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + // Get the window location to restore it from the database. + float fX, fY; + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + jLocations = JsonObjectGet(jLocations, sAssociateType + AI_COPY_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_COPY_NUI, sName + " Copy Settings Menu", + fX, fY, 244.0, fHeight + 12.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Save the associate to the nui. + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oAssociate))); + NuiSetUserData(oPC, nToken, jData); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set all binds, events, and watches. + // Row 1 + NuiSetBind(oPC, nToken, "btn_paste_all_event", JsonBool (TRUE)); + oAssoc = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + NuiSetBind(oPC, nToken, "btn_paste_familiar_event", JsonBool(oAssoc != oAssociate && oAssoc != OBJECT_INVALID)); + oAssoc = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + NuiSetBind(oPC, nToken, "btn_paste_companion_event", JsonBool(oAssoc != oAssociate && oAssoc != OBJECT_INVALID)); + oAssoc = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + NuiSetBind(oPC, nToken, "btn_paste_summons_event", JsonBool(oAssoc != oAssociate && oAssoc != OBJECT_INVALID)); + oAssoc = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + NuiSetBind(oPC, nToken, "btn_paste_dominated_event", JsonBool(oAssoc != oAssociate && oAssoc != OBJECT_INVALID)); + for(nIndex = 1; nIndex < AI_MAX_HENCHMAN; nIndex++) + { + oAssoc = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssoc != OBJECT_INVALID) + { + NuiSetBind(oPC, nToken, "btn_paste_henchman" + IntToString(nIndex) + "_event", JsonBool(oAssoc != oAssociate)); + } + else break; + } +} +void ai_CreatePluginNUI(object oPC) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + int nIndex, nButton; + string sButton; + // Row 1 ******************************************************************* 500 / 73 + json jRow = CreateButton(JsonArray(), "Load Plugins", "btn_load_plugins", 150.0f, 20.0f, -1.0, "btn_load_plugins_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Load Monster Mods", "btn_load_m_mods", 150.0f, 20.0f, -1.0, "btn_load_m_mods_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Check All", "btn_check_plugins", 80.0f, 20.0f, -1.0, "btn_check_plugins_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Clear All", "btn_clear_plugins", 80.0f, 20.0f, -1.0, "btn_clear_plugins_tooltip"); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 ******************************************************************* 500 / 101 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Add Plugin", "btn_add_plugin", 150.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateTextEditBox(jRow, "sPlaceHolder", "txt_plugin", 16, FALSE, 310.0f, 20.0f, "txt_plugin_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + float fHeight = 101.0; + // Row 3+ ****************************************************************** 500 / --- + json jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins"); + nIndex = 0; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + string sName; + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Remove Plugin", "btn_remove_plugin_" + sButton, 150.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + sName = JsonGetString(JsonArrayGet(jPlugin, 2)); + jRow = CreateButton(jRow, sName, "btn_plugin_" + sButton, 290.0f, 20.0f, -1.0, "btn_plugin_" + sButton + "_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_plugin_" + sButton, 25.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + // Get the window location to restore it from the database. + json jLocations = ai_GetAssociateDbJson(oPC, "pc", "locations"); + float fX, fY; + jLocations = JsonObjectGet(jLocations, AI_PLUGIN_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + sName = 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", + fX, fY, 500.0f, fHeight + 12.0f, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Save the associate to the nui for use in 0e_nui + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oPC))); + NuiSetUserData(oPC, nToken, jData); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Row 1 + NuiSetBind(oPC, nToken, "btn_load_plugins_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_load_plugins_tooltip", JsonString(" Load all known PEPS plugins that are in the game files.")); + NuiSetBind(oPC, nToken, "btn_load_m_mods_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_load_m_mods_tooltip", JsonString(" Load all known PEPS monster mods that are in the game files.")); + NuiSetBind(oPC, nToken, "btn_check_plugins_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_check_plugins_tooltip", JsonString(" Add all plugins to the players widget.")); + NuiSetBind(oPC, nToken, "btn_clear_plugins_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_plugins_tooltip", JsonString(" Remove all plugins from the players widget.")); + // Row 2 + NuiSetBind(oPC, nToken, "btn_add_plugin_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_plugin_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_plugin_tooltip", JsonString(" Enter an executable script name.")); + // Row 3+ + nIndex = 0; + int bCheck; + string sText; + jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_remove_plugin_" + sButton + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_event", JsonBool(TRUE)); + bCheck = JsonGetInt(JsonArrayGet(jPlugin, 1)); + if(bCheck < 3) + { + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_check", JsonBool(bCheck)); + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "chbx_plugin_" + sButton + "_check", TRUE); + } + sText = " " + JsonGetString(JsonArrayGet(jPlugin, 2)); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_tooltip", JsonString(sText)); + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } +} +int ai_SpellNotInList(int nSpell, json jSpellArray) +{ + int nMaxArray = JsonGetLength(jSpellArray); + int nIndex; + while(nIndex < nMaxArray) + { + if(nSpell == JsonGetInt(JsonArrayGet(JsonArrayGet(jSpellArray, nIndex), 0))) return FALSE; + nIndex++; + } + return TRUE; +} +json ai_CheckItemAbilities(json jQuickListArray, object oCreature, object oItem, json jSpell_Icon, json jSpell_Text, int bEquiped = FALSE) +{ + // We have established that we can use the item if it is equiped. + if(!bEquiped && !ai_CheckIfCanUseItem(oCreature, oItem)) return jQuickListArray; + int nPerDay, nCharges, nUses, bSaveTalent, nBaseItemType; + int nIprpSubType, nSpell, nLevel, nIPType, nIndex; + string sSpellIcon, sSpellName; + itemproperty ipProp = GetFirstItemProperty(oItem); + json jSpell; + // Lets skip this if there are no properties. + if(!GetIsItemPropertyValid(ipProp)) return jQuickListArray; + // Check for cast spell property and add them to the talent list. + while(GetIsItemPropertyValid(ipProp)) + { + nIPType = GetItemPropertyType(ipProp); + 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((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)); + nBaseItemType = GetBaseItemType(oItem); + if(nBaseItemType == BASE_ITEM_ENCHANTED_SCROLL || + nBaseItemType == BASE_ITEM_SCROLL || + nBaseItemType == BASE_ITEM_SPELLSCROLL) + { + sSpellIcon = Get2DAString("iprp_spells", "Icon", nIprpSubType); + sSpellName = ai_StripColorCodes(GetName(oItem)); + nUses = GetNumStackedItems(oItem); + } + else + { + if(nBaseItemType == BASE_ITEM_ENCHANTED_POTION || + nBaseItemType == BASE_ITEM_POTIONS) + { + sSpellName = ai_StripColorCodes(GetName(oItem)); + nUses = GetNumStackedItems(oItem); + } + else if(nBaseItemType == BASE_ITEM_ENCHANTED_WAND || + nBaseItemType == BASE_ITEM_MAGICWAND || + nBaseItemType == FEAT_CRAFT_WAND) + { + sSpellName = ai_StripColorCodes(GetName(oItem)); + nUses = nCharges; + } + else + { + sSpellName = ai_StripColorCodes(GetName(oItem)) + ": "; + sSpellName += GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(nCharges) nUses = nCharges; + else nUses = nPerDay; + } + sSpellIcon = Get2DAString("spells", "iConResRef", nSpell); + } + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(-1)); // Class is set to -1 for items + jSpell = JsonArrayInsert(jSpell, JsonInt(nUses)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nBaseItemType)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nIprpSubType)); + jSpell = JsonArrayInsert(jSpell, JsonString(GetObjectUUID(oItem))); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + } + } + else if(nIPType == ITEM_PROPERTY_HEALERS_KIT) + { + // Must also have ranks in healing kits. + if(GetSkillRank(SKILL_HEAL, oCreature) > 0) + { + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString("isk_heal")); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(ai_StripColorCodes(GetName(oItem)))); + json jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(SPELL_HEALINGKIT)); + jSpell = JsonArrayInsert(jSpell, JsonInt(-1)); // Class is set to -1 for items + jSpell = JsonArrayInsert(jSpell, JsonInt(GetNumStackedItems(oItem))); + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); + jSpell = JsonArrayInsert(jSpell, JsonInt(GetItemPropertyCostTableValue(ipProp))); + jSpell = JsonArrayInsert(jSpell, JsonString(GetObjectUUID(oItem))); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + } + } + nIndex++; + ipProp = GetNextItemProperty(oItem); + } + SetLocalJson(oCreature, "JSPELL_ICON", jSpell_Icon); + SetLocalJson(oCreature, "JSPELL_NAME", jSpell_Text); + return jQuickListArray; +} +void ai_CreateQuickWidgetSelectionNUI(object oPC, object oAssociate) +{ + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + json jRow = JsonArray(); + // Row 1 Classes************************************************************ 414 / 88 + int nClass, nLevel, nIndex; + string sIndex, sClassIcon, sLevelIcon; + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClass = GetClassByPosition(nIndex, oAssociate); + if(nClass != CLASS_TYPE_INVALID) + { + // This saves the class position in the button id so we can get it later. + sIndex = IntToString(nIndex); + sClassIcon = Get2DAString("classes", "Icon", nClass); + jRow = CreateButtonImage(jRow, sClassIcon, "btn_class_" + sIndex, 35.0f, 35.0f, 0.0, "btn_class_" + sIndex + "_tooltip"); + } + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 (Levels) ********************************************************** 414 / 131 + jRow = CreateButtonImage(JsonArray(), "", "btn_level_11" , 35.0f, 35.0f, 0.0, "btn_level_11_tooltip"); + jRow = CreateButtonImage(jRow, "", "btn_level_10" , 35.0f, 35.0f, 0.0, "btn_level_10_tooltip"); + for(nIndex = 0; nIndex <= 9; nIndex++) + { + // This saves the level in the button id so we can get it later. + sIndex = IntToString(nIndex); + jRow = CreateButtonImage(jRow, "", "btn_level_" + sIndex, 35.0f, 35.0f, 0.0, "btn_level_" + sIndex + "_tooltip"); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 (Spell List)******************************************************* 414 / 433 + json jButton = JsonArray(); + jButton = NuiButton(NuiBind("text_spell")); + jButton = NuiId(jButton, "btn_text_spell"); + json jRectangle = NuiRect(4.0, 4.0, 27.0, 27.0); + json jDrawList = JsonArrayInsert(JsonArray(), NuiDrawListImage(JsonBool(TRUE), NuiBind("icon_spell"), jRectangle, JsonInt(NUI_ASPECT_FILL), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_MIDDLE))); + jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_text")); + jDrawList = JsonArrayInsert(jDrawList, jMetaMagic); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + json jListTemplate = JsonArrayInsert(JsonArray(), NuiListTemplateCell(jButton, 345.0, FALSE)); + json jInfo = NuiButtonImage(JsonString("gui_cg_qstn_mark")); + jInfo = NuiId(jInfo, "btn_info_spell"); + jListTemplate = JsonArrayInsert(jListTemplate, NuiListTemplateCell(jInfo, 35.0, FALSE)); + jRow = JsonArrayInsert(JsonArray(), NuiHeight(NuiList(jListTemplate, NuiBind("icon_spell"), 35.0), 282.0)); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 (Widget Label)***************************************************** 414 / 461 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, "Quick Widget List", "lbl_quick_list", 150.0, 20.0, 0, 0, 0.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 5 (Widget row 1)***************************************************** 414 / 504 + jRow = JsonArray(); + for(nIndex = 0; nIndex < 10; nIndex++) + { + // This saves the index location in the json jWidget in the button id for later use. + sIndex = IntToString(nIndex); + json jButton = NuiButtonImage(NuiBind("btn_widget_" + sIndex + "_image")); + jButton = NuiEnabled(jButton, NuiBind("btn_widget_" + sIndex + "_event")); + jButton = NuiId(jButton, "btn_widget_" + sIndex); + jButton = NuiWidth(NuiHeight(jButton, 35.0), 35.0); + jButton = NuiMargin(jButton, 0.0); + jButton = NuiTooltip(jButton, NuiBind("btn_widget_" + sIndex + "_tooltip")); + json jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); + jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + jRow = JsonArrayInsert(jRow, jButton); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 6 (Widget row 2)***************************************************** 414 / 543 + jRow = JsonArray(); + for(nIndex = 10; nIndex < 20; nIndex++) + { + // This saves the index location in the json jWidget in the button id for later use. + sIndex = IntToString(nIndex); + json jButton = NuiButtonImage(NuiBind("btn_widget_" + sIndex + "_image")); + jButton = NuiEnabled(jButton, NuiBind("btn_widget_" + sIndex + "_event")); + jButton = NuiId(jButton, "btn_widget_" + sIndex); + jButton = NuiWidth(NuiHeight(jButton, 35.0), 35.0); + jButton = NuiMargin(jButton, 0.0); + jButton = NuiTooltip(jButton, NuiBind("btn_widget_" + sIndex + "_tooltip")); + json jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); + jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + jRow = JsonArrayInsert(jRow, jButton); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Get the window location to restore it from the database. + float fX, fY; + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + jLocations = JsonObjectGet(jLocations, sAssociateType + AI_QUICK_WIDGET_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sText, sName = 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", + fX, fY, 414.0, 543.0 + 12.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Set the Layout of the window. + // Save the associate to the nui for use in 0e_nui + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oAssociate))); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + json jSpells; + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + // Temporary fix for error! :/ + if(JsonGetLength(jAIData) == 0) + { + ai_CheckAssociateData(oPC, oAssociate, sAssociateType, TRUE); + jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + jSpells = JsonArray(); + jSpells = JsonArrayInsert(jSpells, JsonInt(1)); + jSpells = JsonArrayInsert(jSpells, JsonInt(10)); + jAIData = JsonArrayInsert(jAIData, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + nLevel = 10; + } + if(JsonGetLength(jAIData) == 9) + { + jSpells = JsonArray(); + jSpells = JsonArrayInsert(jSpells, JsonInt(1)); + jSpells = JsonArrayInsert(jSpells, JsonInt(10)); + jSpells = JsonArrayInsert(jSpells, JsonArray()); + jAIData = JsonArrayInsert(jAIData, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + nLevel = 10; + } + else + { + jSpells = JsonArrayGet(jAIData, 10); + if(JsonGetLength(jSpells) == 0) + { + jSpells = JsonArray(); + jSpells = JsonArrayInsert(jSpells, JsonInt(1)); + jSpells = JsonArrayInsert(jSpells, JsonInt(10)); + jSpells = JsonArrayInsert(jSpells, JsonArray()); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + nLevel = 10; + } + else + { + nClass = JsonGetInt(JsonArrayGet(jSpells, 0)); + nLevel = JsonGetInt(JsonArrayGet(jSpells, 1)); + } + } + if(nClass < 1 || nClass > AI_MAX_CLASSES_PER_CHARACTER) nClass = 1; + nClass = GetClassByPosition(nClass, oAssociate); + // Row 1 & 2 Class & Level + int nSpellLevel, nLevelIndex, nClassIndex, nMaxSpellLevel; + string sClass, sLevel, sLevelImage, sLevelIndex; + NuiSetBind(oPC, nToken, "btn_level_11_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_11_tooltip", JsonString(" Item Powers")); + NuiSetBind(oPC, nToken, "btn_level_11_image", JsonString("ir_attack")); + NuiSetBind(oPC, nToken, "btn_level_10_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_10_tooltip", JsonString(" Special Abilities")); + NuiSetBind(oPC, nToken, "btn_level_10_image", JsonString("dm_god")); + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClassIndex = GetClassByPosition(nIndex, oAssociate); + if(nClassIndex != CLASS_TYPE_INVALID) + { + sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClassIndex))); + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_tooltip", JsonString(" " + sClass)); + if(nClass == nClassIndex) + { + if(StringToInt(Get2DAString("classes", "SpellCaster", nClass))) + { + 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++) + { + sLevelIndex = IntToString(nLevelIndex); + if(nLevelIndex < nMaxSpellLevel) + { + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_event", JsonBool(TRUE)); + if(nLevelIndex == 0) sLevelImage = "ir_cantrips"; + else if(nLevelIndex < 7)sLevelImage = "ir_level" + sLevelIndex; + else sLevelImage = "ir_level789"; + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_image", JsonString(sLevelImage)); + if(nLevelIndex == 0) sLevel = " Cantrips"; + else if(nLevelIndex == 1) sLevel = " First level"; + else if(nLevelIndex == 2) sLevel = " Second level"; + else if(nLevelIndex == 3) sLevel = " Third level"; + else if(nLevelIndex == 4) sLevel = " Fourth level"; + else if(nLevelIndex == 5) sLevel = " Fifth level"; + else if(nLevelIndex == 6) sLevel = " Sixth level"; + else if(nLevelIndex == 7) sLevel = " Seventh level"; + else if(nLevelIndex == 8) sLevel = " Eighth level"; + else if(nLevelIndex == 9) sLevel = " Ninth level"; + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_tooltip", JsonString(" " + sLevel)); + } + else + { + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_event", JsonBool(FALSE)); + } + } + NuiSetBind(oPC, nToken, "btn_level_" + IntToString(nLevel) + "_encouraged", JsonBool(TRUE)); + } + // Default to the abilities tab since they are not a caster. + else + { + if(nLevel < 10) nLevel = 10; + for(nLevelIndex = 0; nLevelIndex <= 9; nLevelIndex++) + { + sLevelIndex = IntToString(nLevelIndex); + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "btn_level_" + sLevelIndex + "_event", JsonBool(FALSE)); + } + NuiSetBind(oPC, nToken, "btn_level_10_encouraged", JsonBool(TRUE)); + } + NuiSetBind(oPC, nToken, "btn_class_" + IntToString(nClass) + "_encouraged", JsonBool(TRUE)); + } + } + } + // Row 3 Items/Abilities/Skills/Spells + int nSpell, nMetaMagic, nDomain, nSubSpell, nSubSpellIndex; + int nSpellSlot, nCounter, nMax2daRow, nFeat; + string sSpellIcon, sSpellName, sMetaMagicText, sClassFeats, sSubSpellIndex; + object oItem; + json jQuickListArray = JsonArray(); + json jSpell; + json jSpell_Icon = JsonArray(); + json jSpell_Text = JsonArray(); + SetLocalJson(oAssociate, "JSPELL_ICON", jSpell_Icon); + SetLocalJson(oAssociate, "JSPELL_NAME", jSpell_Text); + json jMetaMagic_Text = JsonArray(); + // Item powers + if(nLevel == 11) + { + string sSlots; + // Cycle through all the creatures inventory items. + oItem = GetFirstItemInInventory(oAssociate); + 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(sSlots == "0x00000") + { + 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)); + } + } + oItem = GetNextItemInInventory(oAssociate); + } + int nSlot; + // Cycle through all the creatures equiped items. + oItem = GetItemInSlot(nSlot, oAssociate); + while(nSlot < 11) + { + if(oItem != OBJECT_INVALID) + { + jQuickListArray = ai_CheckItemAbilities(jQuickListArray, oAssociate, oItem, jSpell_Icon, jSpell_Text, TRUE); + jSpell_Icon = GetLocalJson(oAssociate, "JSPELL_ICON"); + jSpell_Text = GetLocalJson(oAssociate, "JSPELL_NAME"); + } + oItem = GetItemInSlot(++nSlot, oAssociate); + } + oItem = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oAssociate); + if(oItem != OBJECT_INVALID) + { + jQuickListArray = ai_CheckItemAbilities(jQuickListArray, oAssociate, oItem, jSpell_Icon, jSpell_Text, TRUE); + jSpell_Icon = GetLocalJson(oAssociate, "JSPELL_ICON"); + jSpell_Text = GetLocalJson(oAssociate, "JSPELL_NAME"); + } + DeleteLocalJson(oAssociate, "JSPELL_ICON"); + DeleteLocalJson(oAssociate, "JSPELL_NAME"); + } + // Special abilities and skills. + else if(nLevel == 10) + { + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClassIndex = GetClassByPosition(nIndex, oAssociate); + if(nClassIndex != CLASS_TYPE_INVALID) + { + nCounter = 0; + sClassFeats = Get2DAString("classes", "FeatsTable", nClassIndex); + nMax2daRow = Get2DARowCount(sClassFeats); + while(nCounter < nMax2daRow) + { + if(Get2DAString(sClassFeats, "OnMenu", nCounter) != "0") + { + nFeat = StringToInt(Get2DAString(sClassFeats, "FeatIndex", nCounter)); + if(GetHasFeat(nFeat, oAssociate, TRUE)) + { + // 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(-1)); // Level + jSpell = JsonArrayInsert(jSpell, JsonInt(255)); // MetaMagic + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Domain + 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)); // Level + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // MetaMagic + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); // Domain + jSpell = JsonArrayInsert(jSpell, JsonInt(nFeat)); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + } + } + } + nCounter++; + } + } + } + // 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); + } + else // Anything else is for spells. + { + // Search all memorized spells for the spell. + //SendMessageToPC(oPC, GetName(oAssociate) + " nClass: " + IntToString(nClass) + + // " nLevelSelected: " + IntToString(nLevel) + + // " nMemorizesSpells: " + Get2DAString("classes", "MemorizesSpells", nClass)); + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + int nMaxSlot = GetMemorizedSpellCountByLevel(oAssociate, nClass, nLevel); + while(nSpellSlot < nMaxSlot) + { + nSpell = GetMemorizedSpellId(oAssociate, nClass, nLevel, nSpellSlot); + if(nSpell != -1 && ai_SpellNotInList(nSpell, jQuickListArray)) + { + nMetaMagic = GetMemorizedSpellMetaMagic(oAssociate, nClass, nLevel, nSpellSlot); + nDomain = GetMemorizedSpellIsDomainSpell(oAssociate, nClass, nLevel, nSpellSlot); + // Check for subspells. + nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell1", nSpell)); + if(nSubSpell) + { + for(nSubSpellIndex = 1; nSubSpellIndex < 6; nSubSpellIndex++) + { + sSubSpellIndex = IntToString(nSubSpellIndex); + nSubSpell = StringToInt(Get2DAString("spells", "SubRadSpell" + sSubSpellIndex, nSpell)); + if(nSubSpell && ai_SpellNotInList(nSubSpell, jQuickListArray)) + { + 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)); + sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nMetaMagic, nDomain); + jMetaMagic_Text = JsonArrayInsert(jMetaMagic_Text, JsonString(sMetaMagicText)); + jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSubSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nLevel)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nMetaMagic)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nDomain)); + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); + 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(nClass)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nLevel)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nMetaMagic)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nDomain)); + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + //SendMessageToPC(oPC, "nSpell: " + IntToString(nSpell) + + // " sSpellIcon: " + sSpellIcon + + // " sSpellName: " + sSpellName+ + // " nMaxSlot: " + IntToString(nMaxSlot) + + // " nSpellSlot: " + IntToString(nSpellSlot)); + } + } + ++nSpellSlot; + } + } + // Non-memorized spells. + else + { + int nMaxSlot = GetKnownSpellCount(oAssociate, nClass, nLevel); + while(nSpellSlot < nMaxSlot) + { + nSpell = GetKnownSpellId(oAssociate, nClass, nLevel, nSpellSlot); + if(nSpell != -1)// && ai_SpellNotInList(nSpell, jQuickListArray)) + { + 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)); + jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nLevel)); + jSpell = JsonArrayInsert(jSpell, JsonInt(255)); + jSpell = JsonArrayInsert(jSpell, JsonInt(0)); + jQuickListArray = JsonArrayInsert(jQuickListArray, jSpell); + } + ++nSpellSlot; + } + } + } + NuiSetBind(oPC, nToken, "icon_spell", jSpell_Icon); + NuiSetBind(oPC, nToken, "text_spell", jSpell_Text); + NuiSetBind(oPC, nToken, "metamagic_text", jMetaMagic_Text); + jData = JsonArrayInsert(jData, jQuickListArray); + NuiSetUserData(oPC, nToken, jData); + // Row 4 Quick widget list label. + // Row 5 Quick widget List 1 + ai_PopulateWidgetList(oPC, oAssociate, nToken, JsonArrayGet(jSpells, 2)); +} +void ai_CreateSpellMemorizationNUI(object oPC, object oAssociate) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jRow = JsonArray(); + // Row 1 Classes************************************************************ 414 / 73 + int nClass, bCaster, nIndex; + string sIndex, sClassIcon, sLevelIcon; + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClass = GetClassByPosition(nIndex, oAssociate); + if(nClass != CLASS_TYPE_INVALID) + { + if(StringToInt(Get2DAString("classes", "MemorizesSpells", nClass))) + { + // This saves the class position in the button id so we can get it later. + sIndex = IntToString(nIndex); + sClassIcon = Get2DAString("classes", "Icon", nClass); + jRow = CreateButtonImage(jRow, sClassIcon, "btn_class_" + sIndex, 35.0f, 35.0f, 0.0, "btn_class_" + sIndex + "_tooltip"); + } + } + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 (Levels) ********************************************************** 414 / 116 + jRow = JsonArray(); + for(nIndex = 0; nIndex <= 9; nIndex++) + { + // This saves the level in the button id so we can get it later. + sIndex = IntToString(nIndex); + jRow = CreateButtonImage(jRow, "", "btn_level_" + sIndex, 35.0f, 35.0f, 0.0, "btn_level_" + sIndex + "_tooltip"); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 (Spell List)******************************************************* 414 / 398 + json jButton = JsonArray(); + jButton = NuiButton(NuiBind("text_spell")); + jButton = NuiId(jButton, "btn_text_spell"); + json jRectangle = NuiRect(4.0, 4.0, 27.0, 27.0); + json jDrawList = JsonArrayInsert(JsonArray(), NuiDrawListImage(JsonBool(TRUE), NuiBind("icon_spell"), jRectangle, JsonInt(NUI_ASPECT_FILL), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_MIDDLE))); + //jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + //json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_text")); + //jDrawList = JsonArrayInsert(jDrawList, jMetaMagic); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + json jListTemplate = JsonArrayInsert(JsonArray(), NuiListTemplateCell(jButton, 275.0, FALSE)); + json jInfo = NuiButtonImage(JsonString("gui_cg_qstn_mark")); + jInfo = NuiId(jInfo, "btn_info_spell"); + jListTemplate = JsonArrayInsert(jListTemplate, NuiListTemplateCell(jInfo, 35.0, FALSE)); + jRow = JsonArrayInsert(JsonArray(), NuiHeight(NuiList(jListTemplate, NuiBind("icon_spell"), 35.0), 282.0)); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 (Widget Label)***************************************************** 414 / 426 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + CreateLabel(jRow, "Memorized Spell List", "lbl_spell_list", 150.0, 20.0, 0, 0, 0.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 5 (Memorize slots)*************************************************** 414 / 469 + // Get the class and level selected from the database. + int nClassSelected, nLevelSelected; + json jSpells; + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + // Temporary fix for error! :/ + if(JsonGetLength(jAIData) == 0) + { + ai_CheckAssociateData(oPC, oAssociate, sAssociateType, TRUE); + jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + } + if(JsonGetLength(jAIData) == 9) + { + jSpells = JsonArray(); + jSpells = JsonArrayInsert(jSpells, JsonInt(1)); + jSpells = JsonArrayInsert(jSpells, JsonInt(0)); + jAIData = JsonArrayInsert(jAIData, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + else + { + jSpells = JsonArrayGet(jAIData, 10); + if(JsonGetType(jSpells) == JSON_TYPE_NULL) + { + jSpells = JsonArray(); + jSpells = JsonArrayInsert(jSpells, JsonInt(1)); + jSpells = JsonArrayInsert(jSpells, JsonInt(0)); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + else + { + nClassSelected = JsonGetInt(JsonArrayGet(jSpells, 0)); + nLevelSelected = JsonGetInt(JsonArrayGet(jSpells, 1)); + } + } + // If we left the Quick Use widget on Special Abilities (10) or Items (11) goto level 0 + if(nLevelSelected == 10 || nLevelSelected == 11) + { + nLevelSelected = 0; + jSpells = JsonArraySet(jSpells, 1, JsonInt(0)); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + if(nClassSelected < 1 || nClassSelected > AI_MAX_CLASSES_PER_CHARACTER) nClassSelected = 1; + nClass = GetClassByPosition(nClassSelected, oAssociate); + int nMaxMemorizationSlots = GetMemorizedSpellCountByLevel(oAssociate, nClass, nLevelSelected); + jRow = JsonArray(); + for(nIndex = 0; nIndex < nMaxMemorizationSlots; nIndex++) + { + // This saves the index location of the spell in the list. + sIndex = IntToString(nIndex); + json jButton = NuiButtonImage(NuiBind("btn_memorized_" + sIndex + "_image")); + jButton = NuiEnabled(jButton, NuiBind("btn_memorized_" + sIndex + "_event")); + jButton = NuiId(jButton, "btn_memorized_" + sIndex); + jButton = NuiWidth(NuiHeight(jButton, 35.0), 35.0); + jButton = NuiMargin(jButton, 0.0); + jButton = NuiTooltip(jButton, NuiBind("btn_memorized_" + sIndex + "_tooltip")); + //json jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + //json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); + //jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); + //jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + jRow = JsonArrayInsert(jRow, jButton); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Get the window location to restore it from the database. + float fX, fY; + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + jLocations = JsonObjectGet(jLocations, sAssociateType + AI_SPELL_MEMORIZE_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + string sText, sName = GetName(oAssociate); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_SPELL_MEMORIZE_NUI, sName + " Spell Memorization Menu", + fX, fY, 375.0, 504.0 + 12.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Set the Layout of the window. + // Save the associate to the nui for use in 0e_nui + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oAssociate))); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Row 1 & 2 Class & Level + int nSpellLevel, nIndexLevel, nMaxSpellLevel; + string sClass, sLevel, sLevelImage, sIndexLevel; + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClass = GetClassByPosition(nIndex, oAssociate); + if(nClass != CLASS_TYPE_INVALID) + { + bCaster = StringToInt(Get2DAString("classes", "SpellCaster", nClass)); + if(bCaster) + { + sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_tooltip", JsonString(" " + sClass)); + if(nClassSelected == nIndex) + { + 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++) + { + sIndexLevel = IntToString(nIndexLevel); + if(nIndexLevel < nMaxSpellLevel) + { + if(nIndexLevel == 0) sLevelImage = "ir_cantrips"; + else if(nIndexLevel < 7)sLevelImage = "ir_level" + sIndexLevel; + else sLevelImage = "ir_level789"; + if(nIndexLevel == 0) sLevel = " Cantrips"; + else if(nIndexLevel == 1) sLevel = " First level"; + else if(nIndexLevel == 2) sLevel = " Second level"; + else if(nIndexLevel == 3) sLevel = " Third level"; + else if(nIndexLevel == 4) sLevel = " Fourth level"; + else if(nIndexLevel == 5) sLevel = " Fifth level"; + else if(nIndexLevel == 6) sLevel = " Sixth level"; + else if(nIndexLevel == 7) sLevel = " Seventh level"; + else if(nIndexLevel == 8) sLevel = " Eighth level"; + else if(nIndexLevel == 9) sLevel = " Ninth level"; + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_tooltip", JsonString(" " + sLevel)); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_image", JsonString(sLevelImage)); + } + else + { + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_event", JsonBool(FALSE)); + } + } + NuiSetBind(oPC, nToken, "btn_level_" + IntToString(nLevelSelected) + "_encouraged", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_class_" + IntToString(nClassSelected) + "_encouraged", JsonBool(TRUE)); + } + } + } + } + // Row 3 Spells + int nSpellSlot, nSpell, nMetamagic; + json jSpell; + json jWidget = JsonArrayGet(jSpells, 2); + nClass = GetClassByPosition(nClassSelected, oAssociate); + string sSpellIcon, sSpellName, sMetaMagicText; + json jSpellArray = JsonArray(); + json jSpell_Icon = JsonArray(); + json jSpell_Text = JsonArray(); + json jMetaMagic_Text = JsonArray(); + // List the spells they know from their spellbook. + if(Get2DAString("classes", "SpellbookRestricted", nClass) == "1") + { + int nMaxSpells = GetKnownSpellCount(oAssociate, nClass, nLevelSelected); + //WriteTimestampedLogEntry("Maxspells: " + IntToString(nMaxSpells) + + // " nClass: " + IntToString(nClass) + + // " nLevelSelected: " + IntToString(nLevelSelected)); + while(nSpellSlot < nMaxSpells) + { + nSpell = GetKnownSpellId(oAssociate, nClass, nLevelSelected, nSpellSlot); + if(nSpell != -1) + { + jSpellArray = JsonArrayInsert(jSpellArray, JsonInt(nSpell)); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + //SendMessageToPC(oPC, "SpellBook: nSpell: " + IntToString(nSpell) + + // " sSpellIcon: " + sSpellIcon + + // " sSpellName: " + sSpellName+ + // " nMaxSpells: " + IntToString(nMaxSpells) + + // " nSpellSlot: " + IntToString(nSpellSlot)); + //sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, nClass, nLevelSelected, nSpellSlot); + //jMetaMagic_Text = JsonArrayInsert(jMetaMagic_Text, JsonString(sMetaMagicText)); + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + } + ++nSpellSlot; + } + } + // List the spells from the spells.2da file (they get to choose from them all!). + else + { + string sSpellTableColumn = Get2DAString("classes", "SpellTableColumn", nClass); + int nMaxSpells = Get2DARowCount("spells"); + while(nSpell < nMaxSpells) + { + sLevel = Get2DAString("spells", sSpellTableColumn, nSpell); + if(sLevel != "") + { + if(StringToInt(sLevel) == nLevelSelected) + { + jSpellArray = JsonArrayInsert(jSpellArray, JsonInt(nSpell)); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + } + } + ++nSpell; + } + } + jData = JsonArrayInsert(jData, jSpellArray); + NuiSetUserData(oPC, nToken, jData); + NuiSetBind(oPC, nToken, "icon_spell", jSpell_Icon); + NuiSetBind(oPC, nToken, "text_spell", jSpell_Text); + NuiSetBind(oPC, nToken, "metamagic_text", jMetaMagic_Text); + // Row 4 Spell memorized list label. + // Row 5 Spell memorized List + int nMetaMagic, nDomain; + nIndex = 0; + while(nIndex < nMaxMemorizationSlots) + { + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_event", JsonBool(TRUE)); + if(GetMemorizedSpellId(oAssociate, nClass, nLevelSelected, nIndex) > -1) + { + nSpell = GetMemorizedSpellId(oAssociate, nClass, nLevelSelected, nIndex); + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + //nMetaMagic = 255; + //nDomain = 0; + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_image", JsonString(sSpellIcon)); + NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevelSelected) + ")")); + //sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, -1, -1, -1, nMetaMagic, nDomain); + //NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); + } + else + { + NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); + //NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString("")); + NuiSetBind(oPC, nToken, "btn_memorized_" + sIndex + "_event", JsonBool(FALSE)); + } + ++nIndex; + } +} +void ai_CreateSpellKnownNUI(object oPC, object oAssociate) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jRow = JsonArray(); + // Row 1 Classes************************************************************ 414 / 73 + int nClass, bCaster, nIndex; + string sIndex, sClassIcon, sLevelIcon; + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClass = GetClassByPosition(nIndex, oAssociate); + if(nClass != CLASS_TYPE_INVALID) + { + if(StringToInt(Get2DAString("classes", "SpellbookRestricted", nClass))) + { + // This saves the class position in the button id so we can get it later. + sIndex = IntToString(nIndex); + sClassIcon = Get2DAString("classes", "Icon", nClass); + jRow = CreateButtonImage(jRow, sClassIcon, "btn_class_" + sIndex, 35.0f, 35.0f, 0.0, "btn_class_" + sIndex + "_tooltip"); + } + } + } + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 (Levels) ********************************************************** 414 / 116 + jRow = JsonArray(); + for(nIndex = 0; nIndex <= 9; nIndex++) + { + // This saves the level in the button id so we can get it later. + sIndex = IntToString(nIndex); + jRow = CreateButtonImage(jRow, "", "btn_level_" + sIndex, 35.0f, 35.0f, 0.0, "btn_level_" + sIndex + "_tooltip"); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 (Spell List)******************************************************* 414 / 398 + json jButton = JsonArray(); + jButton = NuiButton(NuiBind("text_spell")); + jButton = NuiId(jButton, "btn_text_spell"); + json jRectangle = NuiRect(4.0, 4.0, 27.0, 27.0); + json jDrawList = JsonArrayInsert(JsonArray(), NuiDrawListImage(JsonBool(TRUE), NuiBind("icon_spell"), jRectangle, JsonInt(NUI_ASPECT_FILL), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_MIDDLE))); + //jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + //json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_text")); + //jDrawList = JsonArrayInsert(jDrawList, jMetaMagic); + jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + json jListTemplate = JsonArrayInsert(JsonArray(), NuiListTemplateCell(jButton, 275.0, FALSE)); + json jInfo = NuiButtonImage(JsonString("gui_cg_qstn_mark")); + jInfo = NuiId(jInfo, "btn_info_spell"); + jListTemplate = JsonArrayInsert(jListTemplate, NuiListTemplateCell(jInfo, 35.0, FALSE)); + jRow = JsonArrayInsert(JsonArray(), NuiHeight(NuiList(jListTemplate, NuiBind("icon_spell"), 35.0), 282.0)); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 (Widget Label)***************************************************** 414 / 426 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + CreateLabel(jRow, "Known Spell List", "lbl_spell_list", 150.0, 20.0, 0, 0, 0.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 5 (Memorize slots)*************************************************** 414 / 469 + // Get the class and level selected from the database. + int nClassSelected, nLevelSelected; + json jSpells; + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + // Temporary fix for error! :/ + if(JsonGetLength(jAIData) == 0) + { + ai_CheckAssociateData(oPC, oAssociate, sAssociateType, TRUE); + jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + } + if(JsonGetLength(jAIData) == 9) + { + jSpells = JsonArray(); + jSpells = JsonArrayInsert(jSpells, JsonInt(1)); + jSpells = JsonArrayInsert(jSpells, JsonInt(0)); + jAIData = JsonArrayInsert(jAIData, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + else + { + jSpells = JsonArrayGet(jAIData, 10); + if(JsonGetType(jSpells) == JSON_TYPE_NULL) + { + jSpells = JsonArray(); + jSpells = JsonArrayInsert(jSpells, JsonInt(1)); + jSpells = JsonArrayInsert(jSpells, JsonInt(0)); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + else + { + nClassSelected = JsonGetInt(JsonArrayGet(jSpells, 0)); + nLevelSelected = JsonGetInt(JsonArrayGet(jSpells, 1)); + } + } + // If we left the Quick Use widget on Special Abilities (10) or Items (11) goto level 0 + if(nLevelSelected == 10 || nLevelSelected == 11) + { + nLevelSelected = 0; + jSpells = JsonArraySet(jSpells, 1, JsonInt(0)); + jAIData = JsonArraySet(jAIData, 10, jSpells); + ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData); + } + if(nClassSelected < 1 || nClassSelected > AI_MAX_CLASSES_PER_CHARACTER) nClassSelected = 1; + nClass = GetClassByPosition(nClassSelected, oAssociate); + jRow = JsonArray(); + for(nIndex = 0; nIndex < 10; nIndex++) + { + // This saves the index location of the spell in the list. + sIndex = IntToString(nIndex); + json jButton = NuiButtonImage(NuiBind("btn_known_" + sIndex + "_image")); + jButton = NuiEnabled(jButton, NuiBind("btn_known_" + sIndex + "_event")); + jButton = NuiId(jButton, "btn_known_" + sIndex); + jButton = NuiWidth(NuiHeight(jButton, 35.0), 35.0); + jButton = NuiMargin(jButton, 0.0); + jButton = NuiTooltip(jButton, NuiBind("btn_known_" + sIndex + "_tooltip")); + //json jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + //json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); + //jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); + //jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + jRow = JsonArrayInsert(jRow, jButton); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Do the second row. + jRow = JsonArray(); + for(nIndex = 10; nIndex < 20; nIndex++) + { + // This saves the index location of the spell in the list. + sIndex = IntToString(nIndex); + json jButton = NuiButtonImage(NuiBind("btn_known_" + sIndex + "_image")); + jButton = NuiEnabled(jButton, NuiBind("btn_known_" + sIndex + "_event")); + jButton = NuiId(jButton, "btn_known_" + sIndex); + jButton = NuiWidth(NuiHeight(jButton, 35.0), 35.0); + jButton = NuiMargin(jButton, 0.0); + jButton = NuiTooltip(jButton, NuiBind("btn_known_" + sIndex + "_tooltip")); + //json jRectangle = NuiRect(4.0, 4.0, 10.0, 10.0); + //json jMetaMagic = NuiDrawListText(JsonBool(TRUE), NuiColor(255, 255, 0), jRectangle, NuiBind("metamagic_" + sIndex + "_text")); + //jDrawList = JsonArrayInsert(JsonArray(), jMetaMagic); + //jButton = NuiDrawList(jButton, JsonBool(TRUE), jDrawList); + jRow = JsonArrayInsert(jRow, jButton); + } + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Get the window location to restore it from the database. + float fX, fY; + json jLocations = ai_GetAssociateDbJson(oPC, sAssociateType, "locations"); + jLocations = JsonObjectGet(jLocations, sAssociateType + AI_SPELL_KNOWN_NUI); + if(JsonGetType(jLocations) == JSON_TYPE_NULL) { fX = -1.0; fY = -1.0; } + else + { + fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + } + string sText, sName = GetName(oAssociate); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + int nToken = SetWindow(oPC, jLayout, sAssociateType + AI_SPELL_KNOWN_NUI, sName + " Spell Known Menu", + fX, fY, 375.0, 539.0 + 12.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + // Set the Layout of the window. + // Save the associate to the nui for use in 0e_nui + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oAssociate))); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Row 1 & 2 Class & Level + int nSpellLevel, nIndexLevel, nMaxSpellLevel, nClassLevel; + string sClass, sLevel, sLevelImage, sIndexLevel, sSpellsGained; + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClass = GetClassByPosition(nIndex, oAssociate); + if(nClass != CLASS_TYPE_INVALID) + { + bCaster = StringToInt(Get2DAString("classes", "SpellbookRestricted", nClass)); + if(bCaster) + { + sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_class_" + sIndex + "_tooltip", JsonString(" " + sClass)); + if(nClassSelected == nIndex) + { + nClassLevel = ai_GetCasterTotalLevel(oAssociate, nClass); + sSpellsGained = Get2DAString("classes", "SpellGainTable", nClass); + nMaxSpellLevel = StringToInt(Get2DAString(sSpellsGained, "NumSpellLevels", nClassLevel - 1)); + for(nIndexLevel = 0; nIndexLevel <= 9; nIndexLevel++) + { + sIndexLevel = IntToString(nIndexLevel); + if(nIndexLevel < nMaxSpellLevel) + { + if(nIndexLevel == 0) sLevelImage = "ir_cantrips"; + else if(nIndexLevel < 7)sLevelImage = "ir_level" + sIndexLevel; + else sLevelImage = "ir_level789"; + if(nIndexLevel == 0) sLevel = " Cantrips"; + else if(nIndexLevel == 1) sLevel = " First level"; + else if(nIndexLevel == 2) sLevel = " Second level"; + else if(nIndexLevel == 3) sLevel = " Third level"; + else if(nIndexLevel == 4) sLevel = " Fourth level"; + else if(nIndexLevel == 5) sLevel = " Fifth level"; + else if(nIndexLevel == 6) sLevel = " Sixth level"; + else if(nIndexLevel == 7) sLevel = " Seventh level"; + else if(nIndexLevel == 8) sLevel = " Eighth level"; + else if(nIndexLevel == 9) sLevel = " Ninth level"; + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_tooltip", JsonString(" " + sLevel)); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_image", JsonString(sLevelImage)); + } + else + { + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "btn_level_" + sIndexLevel + "_event", JsonBool(FALSE)); + } + } + NuiSetBind(oPC, nToken, "btn_level_" + IntToString(nLevelSelected) + "_encouraged", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_class_" + IntToString(nClassSelected) + "_encouraged", JsonBool(TRUE)); + } + } + } + } + // Row 3 Spells + int nSpellSlot, nSpell, nMetamagic; + json jSpell; + json jWidget = JsonArrayGet(jSpells, 2); + nClass = GetClassByPosition(nClassSelected, oAssociate); + string sSpellIcon, sSpellName, sMetaMagicText; + json jSpellArray = JsonArray(); + json jSpell_Icon = JsonArray(); + json jSpell_Text = JsonArray(); + json jMetaMagic_Text = JsonArray(); + // List the spells from the spells.2da file (they get to choose from them all!). + string sSpellTableColumn = Get2DAString("classes", "SpellTableColumn", nClass); + int nMaxSpells = Get2DARowCount("spells"); + while(nSpell < nMaxSpells) + { + sLevel = Get2DAString("spells", sSpellTableColumn, nSpell); + if(sLevel != "") + { + if(StringToInt(sLevel) == nLevelSelected) + { + jSpellArray = JsonArrayInsert(jSpellArray, JsonInt(nSpell)); + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + jSpell_Icon = JsonArrayInsert(jSpell_Icon, JsonString(sSpellIcon)); + jSpell_Text = JsonArrayInsert(jSpell_Text, JsonString(sSpellName)); + } + } + ++nSpell; + } + jData = JsonArrayInsert(jData, jSpellArray); + NuiSetUserData(oPC, nToken, jData); + NuiSetBind(oPC, nToken, "icon_spell", jSpell_Icon); + NuiSetBind(oPC, nToken, "text_spell", jSpell_Text); + NuiSetBind(oPC, nToken, "metamagic_text", jMetaMagic_Text); + // Row 4 Spell known list label. + // Row 5 Spell known List + int nMetaMagic, nDomain, nMaxKnownSlots; + json jClassList = GetLocalJson(oAssociate, AI_CLASS_LIST_JSON); + if(JsonGetType(jClassList) == JSON_TYPE_NULL) + { + jClassList = ObjectToJson(oAssociate); + jClassList = GffGetList(jClassList, "ClassList"); + SetLocalJson(oAssociate, AI_CLASS_LIST_JSON, jClassList); + } + // Get the correct class array. + nIndex = 0; + json jClass = JsonArrayGet(jClassList, nIndex); + while(JsonGetInt(GffGetInt(jClass, "Class")) != nClass) + { + jClass = JsonArrayGet(jClassList, ++nIndex); + } + json jKnownList = GffGetList(jClass, "KnownList" + IntToString(nLevelSelected)); + string sSpellKnownTable = Get2DAString("classes", "SpellKnownTable", nClass); + if(sSpellKnownTable != "") nMaxKnownSlots = StringToInt(Get2DAString(sSpellKnownTable, "SpellLevel" + IntToString(nLevelSelected), nClassLevel - 1)); + else nMaxKnownSlots = 20; + nIndex = 0; + while(nIndex < 20) + { + sIndex = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_event", JsonBool(TRUE)); + if(nIndex < nMaxKnownSlots) + { + jSpell = JsonArrayGet(jKnownList, nIndex); + if(JsonGetType(jSpell) == JSON_TYPE_NULL) + { + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_tooltip", JsonString(" Empty known spell slot")); + } + else + { + nSpell = JsonGetInt(GffGetWord(jSpell, "Spell")); + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + //nMetaMagic = 255; + //nDomain = 0; + sSpellIcon = Get2DAString("spells", "IconResRef", nSpell); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_image", JsonString(sSpellIcon)); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_tooltip", JsonString(" " + sName + " (" + sClass + " / " + IntToString(nLevelSelected) + ")")); + //sMetaMagicText = ai_GetSpellIconAttributes(oAssociate, -1, -1, -1, nMetaMagic, nDomain); + //NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString(sMetaMagicText)); + } + } + else + { + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_image", JsonString("ctl_cg_btn_splvl")); + //NuiSetBind(oPC, nToken, "metamagic_" + sIndex + "_text", JsonString("")); + NuiSetBind(oPC, nToken, "btn_known_" + sIndex + "_event", JsonBool(FALSE)); + } + ++nIndex; + } +} +void ai_CreateDescriptionNUI(object oPC, json jSpell, int nSpell = 0) +{ + // Row 1 ******************************************************************* 500 / 469 + json jRow = CreateImage(JsonArray(), "", "spell_icon", NUI_ASPECT_FIT, NUI_HALIGN_CENTER, NUI_VALIGN_MIDDLE, 40.0, 40.0); + jRow = CreateTextBox(jRow, "spell_text", 380.0, 400.0, FALSE, NUI_SCROLLBARS_Y); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 1 ******************************************************************* 500 / 522 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "OK", "btn_ok", 150.0f, 45.0f); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sName, sIcon, sDescription; + int nFeat, nDescription; + int nClass; + if(nSpell) nClass = 0; + else + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + } + if(nClass == -1) + { + if(nSpell == SPELL_HEALINGKIT) + { + sName = "Healer's Kit"; + sIcon = "isk_heal"; + sDescription = GetStringByStrRef(1720); + } + else + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sIcon = Get2DAString("spells", "IconResRef", nSpell); + nDescription = StringToInt(Get2DAString("spells", "SpellDesc", nSpell)); + if(nDescription) sDescription = GetStringByStrRef(nDescription); + else + { + object oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + sDescription = GetDescription(oItem); + } + } + } + else + { + nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); + if(nFeat) + { + if(nSpell) + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sIcon = Get2DAString("spells", "IconResRef", nSpell); + } + else + { + sName = GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))); + sIcon = Get2DAString("feat", "ICON", nFeat); + } + sDescription = GetStringByStrRef(StringToInt(Get2DAString("feat", "DESCRIPTION", nFeat))); + } + else + { + sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sIcon = Get2DAString("spells", "IconResRef", nSpell); + nDescription = StringToInt(Get2DAString("spells", "SpellDesc", nSpell)); + if(nDescription) sDescription = GetStringByStrRef(nDescription); + else + { + object oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + sDescription = GetDescription(oItem); + } + } + } + int nToken = SetWindow(oPC, jLayout, AI_SPELL_DESCRIPTION_NUI, sName, + -1.0, -1.0, 460.0f, 537.0 + 12.0f, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui"); + json jData = JsonArray(); + jData = JsonArrayInsert(jData, JsonString(ObjectToString(oPC))); + NuiSetUserData(oPC, nToken, jData); + // Row 1 + NuiSetBind(oPC, nToken, "spell_icon_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "spell_icon_image", JsonString(sIcon)); + NuiSetBind(oPC, nToken, "spell_text_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "spell_text", JsonString(sDescription)); + // Row 2 + NuiSetBind(oPC, nToken, "btn_ok_event", JsonBool(TRUE)); +} + diff --git a/_module/nss/0i_menus_dm.nss b/_module/nss/0i_menus_dm.nss new file mode 100644 index 00000000..a2a2cfc8 --- /dev/null +++ b/_module/nss/0i_menus_dm.nss @@ -0,0 +1,1386 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_menus_dm +//////////////////////////////////////////////////////////////////////////////// + Include script for handling NUI menus for DMs. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" +#include "0i_associates" +string ai_GetRandomDMTip() +{ + int nRoll = Random(44); + return Get2DAString("ai_messages", "Text", nRoll); +} +void ai_SetDMWidgetButton(object oPlayer, int nButton, int bOn = TRUE) +{ + int nWidgetButtons = GetLocalInt(oPlayer, sDMWidgetButtonVarname); + string sName = ai_RemoveIllegalCharacters(GetName(oPlayer)); + json jButtons = ai_GetCampaignDbJson("buttons", sName, AI_DM_TABLE); + if(nWidgetButtons == 0) nWidgetButtons = JsonGetInt(JsonArrayGet(jButtons, 0)); + if(bOn) nWidgetButtons = nWidgetButtons | nButton; + else nWidgetButtons = nWidgetButtons & ~nButton; + SetLocalInt(oPlayer, sDMWidgetButtonVarname, nWidgetButtons); + jButtons = JsonArraySet(jButtons, 0, JsonInt(nWidgetButtons)); + ai_SetCampaignDbJson("buttons", jButtons, sName, AI_DM_TABLE); +} +int ai_GetDMWidgetButton(object oPlayer, int nButton) +{ + int nWidgetButtons = GetLocalInt(oPlayer, sDMWidgetButtonVarname); + if(nWidgetButtons == 0) + { + string sName = ai_RemoveIllegalCharacters(GetName(oPlayer)); + json jButtons = ai_GetCampaignDbJson("buttons", sName, AI_DM_TABLE); + nWidgetButtons = JsonGetInt(JsonArrayGet(jButtons, 0)); + } + return nWidgetButtons & nButton; +} +void ai_CreateDMWidgetNUI(object oPC) +{ + // 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)); + int bAIWidgetLock = ai_GetDMWidgetButton(oPC, BTN_DM_WIDGET_LOCK); + int bCmdGroup1 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP1); + int bCmdGroup2 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP2); + int bCmdGroup3 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP3); + int bCmdGroup4 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP4); + int bCmdGroup5 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP5); + int bCmdGroup6 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP6); + int bCmdCamera = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_CAMERA); + int bCmdInventory = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_INVENTORY); + // Get which buttons are activated. + float fHeight = 92.0f; + if(bAIWidgetLock) fHeight = 59.0f; + float fButtons, fWidth = 86.0f; + // ************************************************************************* Width / Height + // Row 1 (buttons)********************************************************** + // Setup the main associate button to use their portrait. + json jButton = NuiEnabled(NuiId (NuiButtonImage(NuiBind("btn_open_main_image")), "btn_open_main"), NuiBind("btn_open_main_event")); + jButton = NuiWidth(jButton, 35.0); + 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)); + json jRow = JsonArrayInsert(JsonArray(), jButton); + if(bCmdGroup1) + { + jRow = CreateButtonImage(jRow, "ir_level1", "btn_cmd_group1", 35.0f, 35.0f, 0.0, "btn_cmd_group1_tooltip"); + fButtons += 1.0; + } + if(bCmdGroup2) + { + jRow = CreateButtonImage(jRow, "ir_level2", "btn_cmd_group2", 35.0f, 35.0f, 0.0, "btn_cmd_group2_tooltip"); + fButtons += 1.0; + } + if(bCmdGroup3) + { + jRow = CreateButtonImage(jRow, "ir_level3", "btn_cmd_group3", 35.0f, 35.0f, 0.0, "btn_cmd_group3_tooltip"); + fButtons += 1.0; + } + if(bCmdGroup4) + { + jRow = CreateButtonImage(jRow, "ir_level4", "btn_cmd_group4", 35.0f, 35.0f, 0.0, "btn_cmd_group4_tooltip"); + fButtons += 1.0; + } + if(bCmdGroup5) + { + jRow = CreateButtonImage(jRow, "ir_level5", "btn_cmd_group5", 35.0f, 35.0f, 0.0, "btn_cmd_group5_tooltip"); + fButtons += 1.0; + } + if(bCmdGroup6) + { + jRow = CreateButtonImage(jRow, "ir_level6", "btn_cmd_group6", 35.0f, 35.0f, 0.0, "btn_cmd_group6_tooltip"); + fButtons += 1.0; + } + if(bCmdCamera) + { + jRow = CreateButtonImage(jRow, "ir_examine", "btn_camera", 35.0f, 35.0f, 0.0, "btn_camera_tooltip"); + fButtons += 1.0; + } + if(bCmdInventory) + { + jRow = CreateButtonImage(jRow, "ir_pickup", "btn_inventory", 35.0f, 35.0f, 0.0, "btn_inventory_tooltip"); + fButtons += 1.0; + } + // Plug in buttons ********************************************************* + int nIndex, bWidget; + string sButton, sIcon; + json jPlugins = ai_UpdatePluginsForDM(oPC); + json jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + bWidget = JsonGetInt(JsonArrayGet(jPlugin, 1)); + if(bWidget) + { + sIcon = JsonGetString(JsonArrayGet(jPlugin, 3)); + sButton = IntToString(nIndex); + jRow = CreateButtonImage(jRow, sIcon, "btn_exe_plugin_" + sButton, 35.0f, 35.0f, 0.0, "btn_exe_plugin_" + sButton + "_tooltip"); + fButtons += 1.0; + } + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + if(fButtons > 1.0f) fWidth = fWidth + ((fButtons - 1.0) * 39.0f); + // Add the row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Get the window location to restore it from the database. + string sName = ai_RemoveIllegalCharacters(GetName(oPC)); + json jLocations = ai_GetCampaignDbJson("locations", sName, AI_DM_TABLE); + jLocations = JsonObjectGet(jLocations, "dm" + AI_WIDGET_NUI); + float fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + if(bAIWidgetLock) + { + fX = fX + 4.0f; + fY = fY + 37.0f; + } + // Set the layout of the window. + json jLayout = NuiCol(jCol); + int nToken; + string sHeal, sText, sRange; + string sDisplayName = GetName(oPC); + if(GetStringRight(sDisplayName, 1) == "s") sDisplayName = sDisplayName + "'"; + else sDisplayName = sDisplayName + "'s"; + if(bAIWidgetLock) nToken = SetWindow(oPC, jLayout, "dm" + AI_WIDGET_NUI, sDisplayName + " Widget", fX, fY, fWidth + 8.0f, fHeight, FALSE, FALSE, FALSE, TRUE, FALSE, "0e_nui_dm"); + else nToken = SetWindow(oPC, jLayout, "dm" + AI_WIDGET_NUI, sDisplayName + " Widget", fX, fY, fWidth + 12.0f, fHeight, FALSE, FALSE, FALSE, TRUE, TRUE, "0e_nui_dm"); + // Set event watches for window inspector and save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set the buttons to show events. + NuiSetBind(oPC, nToken, "btn_open_main_image", JsonString(GetPortraitResRef(oPC) + "s")); + NuiSetBind(oPC, nToken, "btn_open_main_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_open_main_tooltip", JsonString(" " + sDisplayName + " widget menu")); + string sUUID, sText2, sSpeed; + string sAction = " (Left Action/Right Add)"; + if(bCmdGroup1) + { + NuiSetBind(oPC, nToken, "btn_cmd_group1_event", JsonBool(TRUE)); + json jGroup = GetLocalJson(oPC, "DM_GROUP1"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + string sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 1"; sText2 = sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group1_tooltip", JsonString(" " + sText + sText2)); + } + if(bCmdGroup2) + { + NuiSetBind(oPC, nToken, "btn_cmd_group2_event", JsonBool(TRUE)); + json jGroup = GetLocalJson(oPC, "DM_GROUP2"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + string sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 2"; sText2 = sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group2_tooltip", JsonString(" " + sText + sText2)); + } + if(bCmdGroup3) + { + NuiSetBind(oPC, nToken, "btn_cmd_group3_event", JsonBool(TRUE)); + json jGroup = GetLocalJson(oPC, "DM_GROUP3"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + string sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 3"; sText2 = sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group3_tooltip", JsonString(" " + sText + sText2)); + } + if(bCmdGroup4) + { + NuiSetBind(oPC, nToken, "btn_cmd_group4_event", JsonBool(TRUE)); + json jGroup = GetLocalJson(oPC, "DM_GROUP4"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + string sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 4"; sText2 = sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group4_tooltip", JsonString(" " + sText + sText2)); + } + if(bCmdGroup5) + { + NuiSetBind(oPC, nToken, "btn_cmd_group5_event", JsonBool(TRUE)); + json jGroup = GetLocalJson(oPC, "DM_GROUP5"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + string sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 5"; sText2 = sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group5_tooltip", JsonString(" " + sText + sText2)); + } + if(bCmdGroup6) + { + NuiSetBind(oPC, nToken, "btn_cmd_group6_event", JsonBool(TRUE)); + json jGroup = GetLocalJson(oPC, "DM_GROUP6"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + string sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 6"; sText2 = sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group6_tooltip", JsonString(" " + sText + sText2)); + } + if(bCmdCamera) + { + NuiSetBind(oPC, nToken, "btn_camera_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_camera_tooltip", JsonString(" Select new object to have the camera view.")); + } + if(bCmdInventory) + { + NuiSetBind(oPC, nToken, "btn_inventory_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_inventory_tooltip", JsonString(" Open selected creatures inventory.")); + } + /*if(bSearch) + { + NuiSetBind(oPC, nToken, "btn_search_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH)) sText = " Search On"; + else sText = " Search Off"; + NuiSetBind(oPC, nToken, "btn_search_tooltip", JsonString(sText)); + } + if(bStealth) + { + NuiSetBind(oPC, nToken, "btn_stealth_event", JsonBool(TRUE)); + if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH)) sText = " Stealth On"; + else sText = " Stealth Off"; + NuiSetBind(oPC, nToken, "btn_stealth_tooltip", JsonString(sText)); + } */ + nIndex = 0; + string sScript; + jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + bWidget = JsonGetInt(JsonArrayGet(jPlugin, 1)); + if(bWidget) + { + sButton = IntToString(nIndex); + sScript = JsonGetString(JsonArrayGet(jPlugin, 0)); + if(ResManGetAliasFor(sScript, RESTYPE_NCS) == "") + { + sText = " " + sScript + " not found by ResMan!"; + } + else sName = " " + JsonGetString(JsonArrayGet(jPlugin, 2)); + NuiSetBind(oPC, nToken, "btn_exe_plugin_" + sButton + "_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_exe_plugin_" + sButton + "_tooltip", JsonString(sName)); + } + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } +} +void ai_CreateDMOptionsNUI(object oPC) +{ + // Set window to not save until it has been created. + SetLocalInt (oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand (2.0, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + int nMonsterAI = (ResManGetAliasFor("ai_default", RESTYPE_NCS) != ""); + int nAssociateAI = (ResManGetAliasFor("ai_a_default", RESTYPE_NCS) != ""); + string sText = " [Single player]"; + if(AI_SERVER) sText = " [Server]"; + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 500 / 73 + json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, PHILOS_VERSION + sText, "lbl_version ", 510.0f, 20.0f, NUI_HALIGN_CENTER); + 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)); + // 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 = CreateButton(jRow, "Widget Manager", "btn_widget_manager", 160.0f, 20.0f, -1.0, "btn_widget_manager_tooltip"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 ******************************************************************* 500 / 157 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, "SERVER RULES", "lbl_ai_rules", 100.0f, 20.0f, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + float fHeight = 112.0; + // Row 5 ******************************************************************* 500 / --- (28) + // Make the AI options a Group. + json jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_max_henchman", 2, FALSE, 30.0f, 20.0f, "txt_max_henchman_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "Max number of henchmen that is allowed in your party.", "lbl_max_hench", 416.0f, 20.0f, NUI_HALIGN_LEFT, 0, -1.0, "txt_max_henchman_tooltip"); + json jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_xp_scale", 3, FALSE, 40.0f, 20.0f, "txt_xp_scale_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "Modules experience scale.", "lbl_xp_scale", 175.0f, 20.0f, NUI_HALIGN_LEFT, 0, -1.0, "txt_xp_scale_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " scale to party.", "chbx_party_scale", 130.0, 20.0, "chbx_party_scale_tooltip"); + jGroupRow = CreateButton(jGroupRow, "Default", "btn_default_xp", 70.0f, 20.0f, -1.0, "btn_default_xp_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight += 112.0; + if(nMonsterAI || nAssociateAI) + { + jGroupRow = CreateCheckBox(JsonArray(), " Creatures will use advanced combat movement.", "chbx_advanced_movement", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Use item level restrictions for creatures [Default is off].", "chbx_ilr", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Creatures can use the skill Use Magic Device.", "chbx_umd", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Creatures can use Healing kits.", "chbx_use_healingkits", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Moral checks, wounded creatures may flee during combat.", "chbx_moral", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), " Spells the AI will not use:", "lbl_restrict_spells", 190.0, 20.0, NUI_HALIGN_LEFT); + jGroupRow = CreateCheckBox(jGroupRow, " Darkness", "chbx_darkness", 90.0, 20.0, "chbx_darkness_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " Dispels", "chbx_dispels", 90.0, 20.0, "chbx_dispels_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, " Time Stop", "chbx_timestop", 90.0, 20.0, "chbx_timestop_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight += 168.0; + } + if(nMonsterAI) + { + jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_ai_difficulty", 3, FALSE, 40.0f, 20.0f, "txt_ai_difficulty_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "% chance monsters will attack the weakest target.", "lbl_ai_difficulty", 406.0f, 20.0f, NUI_HALIGN_LEFT, 0, -1.0, "txt_ai_difficulty_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + 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); + 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)); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters can use tactics (ambush, defensive, flanker, etc).", "chbx_ambush_monsters", 450.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "Add ", "lbl_inc_enc", 30.0, 20.0, NUI_HALIGN_LEFT, 0, -1.0); + jGroupRow = CreateTextEditBox(jGroupRow, "sPlaceHolder", "txt_inc_enc", 4, FALSE, 55.0f, 20.0f, "txt_inc_enc_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "monsters per spawned encounter monster.", "lbl_inc_enc", 357.0, 20.0, NUI_HALIGN_LEFT, NUI_VALIGN_MIDDLE, 0.0, "txt_inc_enc_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateTextEditBox(JsonArray(), "sPlaceHolder", "txt_inc_hp", 3, FALSE, 40.0f, 20.0f, "txt_inc_hp_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "% increase in all monster's hitpoints.", "lbl_inc_hp", 406.0, 20.0, NUI_HALIGN_LEFT); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "***** WARNING! The options below may break the module! *****", "lbl_warning", 450.0f, 20.0f, NUI_HALIGN_LEFT, 0, 0.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters can wander upto ", "chbx_wander", 220.0, 20.0, "chbx_warning_tooltip"); + jGroupRow = CreateTextEditBox(jGroupRow, "sPlaceHolder", "txt_wander_distance", 2, FALSE, 35.0f, 20.0f, "chbx_warning_tooltip"); + jGroupRow = CreateLabel(jGroupRow, "meters and ", "lbl_wander_distance", 80.0f, 20.0f, NUI_HALIGN_LEFT, NUI_VALIGN_MIDDLE, 0.0, "chbx_warning_tooltip"); + jGroupRow = CreateCheckBox(jGroupRow, "open doors.", "chbx_open_doors", 100.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Monsters can summon companions.", "chbx_companions", 450.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Summoned associates to remain after masters death.", "chbx_perm_assoc", 450.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateCheckBox(JsonArray(), " Make enemy corpses remain.", "chbx_corpses_stay", 450.0, 20.0, "chbx_warning_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "", "lbl_perc_dist", 450.0f, 20.0f, NUI_HALIGN_LEFT, 0, 0.0, "lbl_perc_dist_tooltip"); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight += 364.0; + } + jRow = JsonArrayInsert(JsonArray(), NuiGroup(NuiCol(jGroupCol))); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Get the window location to restore it from the database. + string sName = ai_RemoveIllegalCharacters(GetName(oPC)); + json jLocations = ai_GetCampaignDbJson("locations", sName, AI_DM_TABLE); + jLocations = JsonObjectGet(jLocations, "dm" + AI_MAIN_NUI); + float fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + sName = GetName(oPC); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int nToken = SetWindow(oPC, jLayout, "dm" + AI_MAIN_NUI, sName + " PEPS Main Menu", + fX, fY, 534.0f, fHeight, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui_dm"); + // Save the associate to the nui for use in 0e_nui + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oPC))); + NuiSetUserData(oPC, nToken, jData); + object oModule = GetModule(); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set all binds, events, and watches. + // Row 1 - Version label. + // Row 2 + int nUsing; + // 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)); + // Row 3 + NuiSetBind(oPC, nToken, "btn_plugin_manager_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_plugin_manager_tooltip", JsonString(" Manages external executable scripts.")); + 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 + // Row 4 + NuiSetBind(oPC, nToken, "txt_max_henchman_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_max_henchman", JsonString(IntToString(GetLocalInt(oModule, AI_RULE_MAX_HENCHMAN)))); + NuiSetBindWatch (oPC, nToken, "txt_max_henchman", TRUE); + NuiSetBind(oPC, nToken, "txt_max_henchman_tooltip", JsonString(" Set max number of henchman allowed (1-12).")); + NuiSetBind(oPC, nToken, "txt_xp_scale_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_xp_scale", JsonString(IntToString(GetModuleXPScale()))); + NuiSetBindWatch (oPC, nToken, "txt_xp_scale", TRUE); + NuiSetBind(oPC, nToken, "txt_xp_scale_tooltip", JsonString(" Set the modules XP scale (0 - 200) Normal D&D is 10.")); + NuiSetBind(oPC, nToken, "chbx_party_scale_check", JsonBool(GetLocalInt(oModule, AI_RULE_PARTY_SCALE))); + NuiSetBindWatch(oPC, nToken, "chbx_party_scale_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_party_scale_event", JsonBool(TRUE)); + sText = IntToString(GetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP)); + NuiSetBind(oPC, nToken, "chbx_party_scale_tooltip", JsonString(" PEPS adjusts your XP based on party size from (" + sText + ").")); + NuiSetBind(oPC, nToken, "btn_default_xp_event", JsonBool(TRUE)); + sText = IntToString(GetLocalInt(oModule, AI_RULE_DEFAULT_XP_SCALE)); + NuiSetBind(oPC, nToken, "btn_default_xp_tooltip", JsonString(" Reset the Modules XP to (" + sText + ").")); + if(nMonsterAI) + { + 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))); + NuiSetBindWatch(oPC, nToken, "chbx_buff_monsters_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_buff_monsters_event", JsonBool(TRUE)); + 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)); + NuiSetBind(oPC, nToken, "chbx_ambush_monsters_check", JsonBool(GetLocalInt(oModule, AI_RULE_AMBUSH))); + NuiSetBindWatch(oPC, nToken, "chbx_ambush_monsters_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ambush_monsters_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_companions_check", JsonBool(GetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS))); + NuiSetBindWatch(oPC, nToken, "chbx_companions_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_companions_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_companions_tooltip", JsonString(" ** This will break some modules! ** See Readme for issues!")); + NuiSetBind(oPC, nToken, "chbx_perm_assoc_check", JsonBool(GetLocalInt(oModule, AI_RULE_PERM_ASSOC))); + NuiSetBindWatch(oPC, nToken, "chbx_perm_assoc_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_perm_assoc_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_corpses_stay_check", JsonBool(GetLocalInt(oModule, AI_RULE_CORPSES_STAY))); + NuiSetBindWatch(oPC, nToken, "chbx_corpses_stay_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_corpses_stay_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_corpses_stay_tooltip", JsonString(" ** This will break some modules! ** See Readme for issues!")); + NuiSetBind(oPC, nToken, "txt_perception_distance_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_perception_distance", JsonString(FloatToString(GetLocalFloat(oModule, AI_RULE_PERCEPTION_DISTANCE), 0, 0))); + NuiSetBindWatch(oPC, nToken, "txt_perception_distance", TRUE); + NuiSetBind(oPC, nToken, "txt_perception_distance_tooltip", JsonString(" Range [10 to 60 meters] from the player.")); + NuiSetBindWatch(oPC, nToken, "lbl_perc_dist", TRUE); + int nPercDist = GetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE); + if(nPercDist < 8 || nPercDist > 11) + { + nPercDist = 11; + SetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE, 11); + } + if(nPercDist == 8) sText = " Monster perception: Short [10 Sight / 10 Listen]"; + else if(nPercDist == 9) sText = " Monster perception: Medium [20 Sight / 20 Listen]"; + else if(nPercDist == 10) sText = " Monster perception: Long [35 Sight / 20 Listen]"; + else sText = " Monster perception: Default [Monster's default values]"; + NuiSetBind(oPC, nToken, "lbl_perc_dist_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "lbl_perc_dist_tooltip", JsonString(" Use the mouse wheel to change values.")); + int bWander = GetLocalInt(oModule, AI_RULE_WANDER); + NuiSetBind(oPC, nToken, "chbx_wander_check", JsonBool(bWander)); + NuiSetBindWatch(oPC, nToken, "chbx_wander_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_wander_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_wander_distance_event", JsonBool(bWander)); + NuiSetBind(oPC, nToken, "txt_wander_distance", JsonString(FloatToString(GetLocalFloat(oModule, AI_RULE_WANDER_DISTANCE), 0, 0))); + NuiSetBindWatch(oPC, nToken, "txt_wander_distance", TRUE); + NuiSetBind(oPC, nToken, "chbx_wander_tooltip", JsonString(" ** This will break some modules! ** See Readme for issues!")); + NuiSetBind(oPC, nToken, "chbx_open_doors_check", JsonBool(GetLocalInt(oModule, AI_RULE_OPEN_DOORS))); + NuiSetBindWatch(oPC, nToken, "chbx_open_doors_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_open_doors_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_open_doors_tooltip", JsonString(" This allows monsters to open doors to hunt you down!")); + NuiSetBind(oPC, nToken, "txt_inc_enc_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_inc_enc_tooltip", JsonString(" Spawns one extra monster per counter above 1. Adds value to counter per encounter monster spawned.")); + NuiSetBind(oPC, nToken, "txt_inc_enc", JsonString(FloatToString(GetLocalFloat(oModule, AI_INCREASE_ENC_MONSTERS), 0, 2))); + NuiSetBindWatch(oPC, nToken, "txt_inc_enc", TRUE); + NuiSetBind(oPC, nToken, "txt_inc_hp_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_inc_hp", JsonString(IntToString(GetLocalInt(oModule, AI_INCREASE_MONSTERS_HP)))); + NuiSetBindWatch(oPC, nToken, "txt_inc_hp", TRUE); + } + if(nMonsterAI || nAssociateAI) + { + NuiSetBind(oPC, nToken, "chbx_moral_check", JsonBool(GetLocalInt(oModule, AI_RULE_MORAL_CHECKS))); + NuiSetBindWatch (oPC, nToken, "chbx_moral_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_moral_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_advanced_movement_check", JsonBool(GetLocalInt(oModule, AI_RULE_ADVANCED_MOVEMENT))); + NuiSetBindWatch (oPC, nToken, "chbx_advanced_movement_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_advanced_movement_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_ilr_check", JsonBool(GetLocalInt(oModule, AI_RULE_ILR))); + NuiSetBindWatch (oPC, nToken, "chbx_ilr_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ilr_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_umd_check", JsonBool(GetLocalInt(oModule, AI_RULE_ALLOW_UMD))); + NuiSetBindWatch (oPC, nToken, "chbx_umd_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_umd_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_use_healingkits_check", JsonBool(GetLocalInt(oModule, AI_RULE_HEALERSKITS))); + NuiSetBindWatch (oPC, nToken, "chbx_use_healingkits_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_use_healingkits_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_darkness_check", JsonBool(ai_SpellRestricted(SPELL_DARKNESS))); + NuiSetBindWatch (oPC, nToken, "chbx_darkness_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_darkness_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_darkness_tooltip", JsonString(" AI will not use the Darkness spell in combat.")); + NuiSetBind(oPC, nToken, "chbx_dispels_check", JsonBool(ai_SpellRestricted(SPELL_DISPEL_MAGIC))); + NuiSetBindWatch (oPC, nToken, "chbx_dispels_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_dispels_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_dispels_tooltip", JsonString(" AI will not use any of the Dispel spells in combat.")); + NuiSetBind(oPC, nToken, "chbx_timestop_check", JsonBool(ai_SpellRestricted(SPELL_TIME_STOP))); + NuiSetBindWatch (oPC, nToken, "chbx_timestop_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_timestop_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "chbx_timestop_tooltip", JsonString(" AI will not use the Time Stop spell in combat.")); + } +} +void ai_CreateDMCommandNUI(object oPC) +{ + SetLocalInt(oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand(0.5f, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 500 / 73 + json jRow = CreateButtonSelect(JsonArray(), "Lock Widget", "btn_widget_lock", 200.0, 20.0, "btn_widget_lock_tooltip"); + jRow = CreateLabel(jRow, "", "blank_label_1", 25.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + 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); + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 ******************************************************************* 500 / 101 + jRow = CreateButton(JsonArray(), "", "btn_cmd_group1", 200.0, 20.0, -1.0, "btn_cmd_group1_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_group1", 25.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "", "btn_cmd_group2", 200.0, 20.0, -1.0, "btn_cmd_group2_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_group2", 25.0, 20.0); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 ******************************************************************* 500 / 129 + jRow = CreateButton(JsonArray(), "", "btn_cmd_group3", 200.0, 20.0, -1.0, "btn_cmd_group3_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_group3", 25.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "", "btn_cmd_group4", 200.0, 20.0, -1.0, "btn_cmd_group4_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_group4", 25.0, 20.0); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 ******************************************************************* 500 / 157 + jRow = CreateButton(JsonArray(), "", "btn_cmd_group5", 200.0, 20.0, -1.0, "btn_cmd_group5_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_group5", 25.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "", "btn_cmd_group6", 200.0, 20.0, -1.0, "btn_cmd_group6_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_group6", 25.0, 20.0); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + float fHeight = 157.0; + // Row 5 ******************************************************************* 500 / --- + jRow = CreateButton(JsonArray(), "Toggle Camera Focus", "btn_camera", 200.0, 20.0, -1.0, "btn_camera_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_camera", 25.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Open/Close Inventory", "btn_inventory", 200.0, 20.0, -1.0, "btn_inventory_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_inventory", 25.0, 20.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + // Row 6+ ****************************************************************** 500 / --- + json jDMPlugins = ai_UpdatePluginsForDM(oPC); + // Set the plugins the dm can use. + int nIndex; + string sButton, sName; + json jPlugin = JsonArrayGet(jDMPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + sName = JsonGetString(JsonArrayGet(jPlugin, 2)); + jRow = CreateButton(JsonArray(), sName, "btn_plugin_" + sButton, 200.0f, 20.0f, -1.0, "btn_plugin_" + sButton + "_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_plugin_" + sButton, 25.0, 20.0, "chbx_plugin_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jPlugin = JsonArrayGet(jDMPlugins, ++nIndex); + if(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + sName = JsonGetString(JsonArrayGet(jPlugin, 2)); + jRow = CreateButton(jRow, sName, "btn_plugin_" + sButton, 200.0f, 20.0f, -1.0, "btn_plugin_" + sButton + "_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_plugin_" + sButton, 25.0, 20.0, "chbx_plugin_tooltip"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + } + else + { + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + break; + } + jPlugin = JsonArrayGet(jDMPlugins, ++nIndex); + } + // Row 7 ****************************************************************** 500 / --- + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, "", "lbl_info_1", 475.0, 20.0, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight = fHeight + 28.0; + // Get the window location to restore it from the database. + sName = ai_RemoveIllegalCharacters(GetName(oPC)); + json jLocations = ai_GetCampaignDbJson("locations", sName, AI_DM_TABLE); + jLocations = JsonObjectGet(jLocations, "dm" + AI_COMMAND_NUI); + float fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sDMName = GetName(oPC); + if(GetStringRight(sDMName, 1) == "s") sDMName = sDMName + "'"; + else sDMName = sDMName + "'s"; + int nToken = SetWindow(oPC, jLayout, "dm" + AI_COMMAND_NUI, sDMName + " Command Menu", + fX, fY, 500.0, fHeight + 12.0, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui_dm"); + // Get which buttons are activated. + int bAIWidgetLock = ai_GetDMWidgetButton(oPC, BTN_DM_WIDGET_LOCK); + int bCmdGroup1 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP1); + int bCmdGroup2 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP2); + int bCmdGroup3 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP3); + int bCmdGroup4 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP4); + int bCmdGroup5 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP5); + int bCmdGroup6 = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_GROUP6); + int bCmdCamera = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_CAMERA); + int bCmdInventory = ai_GetDMWidgetButton(oPC, BTN_DM_CMD_INVENTORY); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Set all binds, events, and watches. + // Row 1 + NuiSetBind(oPC, nToken, "btn_widget_lock_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_widget_lock", JsonBool(bAIWidgetLock)); + NuiSetBind(oPC, nToken, "btn_widget_lock_tooltip", JsonString( + " Locks widget to the current location.")); + NuiSetBind(oPC, nToken, "btn_main_menu_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_main_menu", JsonInt(TRUE)); + NuiSetBind(oPC, nToken, "btn_main_menu_tooltip", JsonString(" Server menu options")); + NuiSetBind(oPC, nToken, "btn_group_options_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_group_options", JsonInt(TRUE)); + //NuiSetBind(oPC, nToken, "btn_empty_button_event", JsonBool (TRUE)); + //NuiSetBind(oPC, nToken, "btn_empty_button", JsonInt(TRUE)); + //sText = " Copy AI and command settings for one creature to others."; + //NuiSetBind(oPC, nToken, "btn_empty_button_tooltip", JsonString(sText)); + // Row 2 + NuiSetBind(oPC, nToken, "chbx_cmd_group1_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_group1_check", JsonBool (bCmdGroup1)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_group1_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cmd_group1_event", JsonBool (TRUE)); + string sText, sText2, sSpeed; + string sAction = " (Left Action/Right Add)"; + json jGroup = GetLocalJson(oPC, "DM_GROUP1"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + string sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 1"; sText2 = sText + sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sText + sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group1_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_cmd_group1_tooltip", JsonString(" " + sText2)); + NuiSetBind(oPC, nToken, "chbx_cmd_group2_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_group2_check", JsonBool (bCmdGroup2)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_group2_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cmd_group2_event", JsonBool (TRUE)); + jGroup = GetLocalJson(oPC, "DM_GROUP2"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 2"; sText2 = sText + sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sText + sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group2_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_cmd_group2_tooltip", JsonString(" " + sText2)); + // Row 3 + NuiSetBind(oPC, nToken, "chbx_cmd_group3_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_group3_check", JsonBool (bCmdGroup3)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_group3_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cmd_group3_event", JsonBool (TRUE)); + jGroup = GetLocalJson(oPC, "DM_GROUP3"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 3"; sText2 = sText + sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sText + sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group3_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_cmd_group3_tooltip", JsonString(" " + sText2)); + NuiSetBind(oPC, nToken, "chbx_cmd_group4_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_group4_check", JsonBool (bCmdGroup4)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_group4_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cmd_group4_event", JsonBool (TRUE)); + jGroup = GetLocalJson(oPC, "DM_GROUP4"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 4"; sText2 = sText + sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sText + sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group4_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_cmd_group4_tooltip", JsonString(" " + sText2)); + // Row 4 + NuiSetBind(oPC, nToken, "chbx_cmd_group5_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_group5_check", JsonBool (bCmdGroup5)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_group5_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cmd_group5_event", JsonBool (TRUE)); + jGroup = GetLocalJson(oPC, "DM_GROUP5"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 5"; sText2 = sText + sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sText + sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group5_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_cmd_group5_tooltip", JsonString(" " + sText2)); + NuiSetBind(oPC, nToken, "chbx_cmd_group6_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_cmd_group6_check", JsonBool (bCmdGroup6)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_group6_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cmd_group6_event", JsonBool (TRUE)); + jGroup = GetLocalJson(oPC, "DM_GROUP6"); + if(JsonGetInt(JsonArrayGet(jGroup, 0)) == 0) sSpeed = " [Walk]"; + else sSpeed = " [Run]"; + sUUID = JsonGetString(JsonArrayGet(jGroup, 1)); + if(sUUID == "") { sText = "Group 6"; sText2 = sText + sAction; } + else { sText = GetName(GetObjectByUUID(sUUID)) + "'s group"; sText2 = sText + sSpeed; } + NuiSetBind(oPC, nToken, "btn_cmd_group6_label", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_cmd_group6_tooltip", JsonString(" " + sText2)); + // Row 5 + NuiSetBind(oPC, nToken, "chbx_camera_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_camera_check", JsonBool (bCmdCamera)); + NuiSetBindWatch (oPC, nToken, "chbx_camera_check", TRUE); + NuiSetBind(oPC, nToken, "btn_camera_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_camera_tooltip", JsonString ( + " Toggle camera view for " + sDMName)); + NuiSetBind(oPC, nToken, "chbx_inventory_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "chbx_inventory_check", JsonBool (bCmdInventory)); + NuiSetBindWatch (oPC, nToken, "chbx_inventory_check", TRUE); + NuiSetBind(oPC, nToken, "btn_inventory_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_inventory_tooltip", JsonString ( + " Open " + sDMName + " inventory")); + // Row 6+ + nIndex = 0; + int bWidget; + jPlugin = JsonArrayGet(jDMPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_event", JsonBool(TRUE)); + bWidget = JsonGetInt(JsonArrayGet(jPlugin, 1)); + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_check", JsonBool(bWidget)); + NuiSetBindWatch (oPC, nToken, "chbx_plugin_" + sButton + "_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_event", JsonBool(TRUE)); + sText = " " + JsonGetString(JsonArrayGet(jPlugin, 2)); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_tooltip", JsonString(sText)); + jPlugin = JsonArrayGet(jDMPlugins, ++nIndex); + } + NuiSetBind(oPC, nToken, "chbx_plugin_tooltip", JsonString(" Adds the plugin to your widget.")); + // Row 7 + sText = ai_GetRandomDMTip(); + NuiSetBind(oPC, nToken, "lbl_info_1_label", JsonString(sText)); +} +void ai_CreateDMPluginManagerNUI(object oPC) +{ + SetLocalInt(oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand(0.5f, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + // Row 1 ******************************************************************* 500 / 73 + json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Load All Plugins", "btn_load_plugins", 150.0f, 20.0f, -1.0, "btn_load_plugins_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Check All", "btn_check_plugins", 150.0f, 20.0f, -1.0, "btn_check_plugins_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Clear All", "btn_clear_plugins", 150.0f, 20.0f, -1.0, "btn_clear_plugins_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 ******************************************************************* 500 / 101 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Add Plugin", "btn_add_plugin", 150.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateTextEditBox(jRow, "sPlaceHolder", "txt_plugin", 16, FALSE, 310.0f, 20.0f, "txt_plugin_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + float fHeight = 101.0; + // Row 3+ ****************************************************************** 500 / --- + json jPlugins = ai_GetCampaignDbJson("plugins"); + int nIndex = 0; + json jPlugin = JsonArrayGet(jPlugins, nIndex); + string sName, sButton; + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Remove Plugin", "btn_remove_plugin_" + sButton, 150.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + sName = JsonGetString(JsonArrayGet(jPlugin, 2)); + jRow = CreateButton(jRow, sName, "btn_plugin_" + sButton, 290.0f, 20.0f, -1.0, "btn_plugin_" + sButton + "_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_plugin_" + sButton, 25.0, 20.0); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 28.0; + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + // Get the window location to restore it from the database. + sName = ai_RemoveIllegalCharacters(GetName(oPC)); + json jLocations = ai_GetCampaignDbJson("locations", sName, AI_DM_TABLE); + jLocations = JsonObjectGet(jLocations, "dm" + AI_PLUGIN_NUI); + float fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + sName = GetName(oPC); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int nToken = SetWindow(oPC, jLayout, "dm" + AI_PLUGIN_NUI, sName + " PEPS Plugin Manager", + fX, fY, 500.0f, fHeight + 12.0f, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui_dm"); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Row 1 + NuiSetBind(oPC, nToken, "btn_load_plugins_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_load_plugins_tooltip", JsonString(" Load all known PEPS plugins that are in the game files.")); + NuiSetBind(oPC, nToken, "btn_check_plugins_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_check_plugins_tooltip", JsonString(" Add all plugins to the players widget.")); + NuiSetBind(oPC, nToken, "btn_clear_plugins_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_plugins_tooltip", JsonString(" Remove all plugins from the players widget.")); + // Row 2 + NuiSetBind(oPC, nToken, "btn_add_plugin_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_plugin_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_plugin_tooltip", JsonString(" Enter an executable script name.")); + // Row 3+ + nIndex = 0; + int bCheck; + string sText; + jPlugin = JsonArrayGet(jPlugins, nIndex); + while(JsonGetType(jPlugin) != JSON_TYPE_NULL) + { + sButton = IntToString(nIndex); + NuiSetBind(oPC, nToken, "btn_remove_plugin_" + sButton + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_event", JsonBool(TRUE)); + bCheck = JsonGetInt(JsonArrayGet(jPlugin, 1)); + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_check", JsonBool(bCheck)); + NuiSetBind(oPC, nToken, "chbx_plugin_" + sButton + "_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "chbx_plugin_" + sButton + "_check", TRUE); + sText = " " + JsonGetString(JsonArrayGet(jPlugin, 2)); + NuiSetBind(oPC, nToken, "btn_plugin_" + sButton + "_tooltip", JsonString(sText)); + jPlugin = JsonArrayGet(jPlugins, ++nIndex); + } + NuiSetBind(oPC, nToken, "chbx_plugin_tooltip", JsonString(" Allows players to use this plugin.")); +} +void ai_CreateDMWidgetManagerNUI(object oPC) +{ + SetLocalInt(oPC, AI_NO_NUI_SAVE, TRUE); + DelayCommand(0.5f, DeleteLocalInt (oPC, AI_NO_NUI_SAVE)); + // Row 1 ******************************************************************* 575 / 73 + json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Check All", "btn_check_buttons", 150.0f, 20.0f, -1.0, "btn_check_buttons_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Clear All", "btn_clear_buttons", 150.0f, 20.0f, -1.0, "btn_clear_buttons_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 ******************************************************************* 575 / 96 + jRow = CreateLabel(JsonArray(), "This menu manages the PEPS buttons a player may have access to.", "lbl_info1", 636.0, 15.0); + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 ******************************************************************* 575 / 119 + 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(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"); + + jRow = CreateButtonImage(jRow, "ir_guard", "btn_cmd_guard", 35.0f, 35.0f, 0.0, "btn_cmd_guard_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_guard", 25.0, 20.0, "btn_cmd_guard_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_standground", "btn_cmd_hold", 35.0f, 35.0f, 0.0, "btn_cmd_hold_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_hold", 25.0, 20.0, "btn_cmd_hold_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_attacknearest", "btn_cmd_attack", 35.0f, 35.0f, 0.0, "btn_cmd_attack_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_attack", 25.0, 20.0, "btn_cmd_attack_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_follow", "btn_cmd_follow", 35.0f, 35.0f, 0.0, "btn_cmd_follow_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_follow", 25.0, 20.0, "btn_cmd_follow_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_dmchat", "btn_follow_target", 35.0f, 35.0f, 0.0, "btn_follow_target_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_follow_target", 25.0, 20.0, "btn_follow_target_tooltip"); + + jRow = CreateButtonImage(jRow, "ife_foc_search", "btn_cmd_search", 35.0f, 35.0f, 0.0, "btn_cmd_search_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_search", 25.0, 20.0, "btn_cmd_search_tooltip"); + + jRow = CreateButtonImage(jRow, "ife_foc_hide", "btn_cmd_stealth", 35.0f, 35.0f, 0.0, "btn_cmd_stealth_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cmd_stealth", 25.0, 20.0, "btn_cmd_stealth_tooltip"); + + 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(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"); + + jRow = CreateButtonImage(jRow, "isk_lore", "btn_spell_memorize", 35.0f, 35.0f, 0.0, "btn_spell_memorize_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_spell_memorize", 25.0, 20.0, "btn_spell_memorize_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_cantrips", "btn_buff_short", 35.0f, 35.0f, 0.0, "btn_buff_short_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_short", 25.0, 20.0, "btn_buff_short_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_cast", "btn_buff_long", 35.0f, 35.0f, 0.0, "btn_buff_long_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_long", 25.0, 20.0, "btn_buff_long_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_level789", "btn_buff_all", 35.0f, 35.0f, 0.0, "btn_buff_all_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_all", 25.0, 20.0, "btn_buff_all_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_rest", "btn_buff_rest", 35.0f, 35.0f, 0.0, "btn_buff_rest_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_buff_rest", 25.0, 20.0, "btn_buff_rest_tooltip"); + + jRow = CreateButtonImage(jRow, "dm_jump", "btn_jump_to", 35.0f, 35.0f, 0.0, "btn_jump_to_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_jump_to", 25.0, 20.0, "btn_jump_to_tooltip"); + + jRow = CreateButtonImage(jRow, "dm_limbo", "btn_ghost_mode", 35.0f, 35.0f, 0.0, "btn_ghost_mode_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ghost_mode", 25.0, 20.0, "btn_ghost_mode_tooltip"); + + 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(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"); + + jRow = CreateButtonImage(jRow, "ife_animal", "btn_companion", 35.0f, 35.0f, 0.0, "btn_companion_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_companion", 25.0, 20.0, "btn_companion_tooltip"); + + jRow = CreateButtonImage(jRow, "dm_ai", "btn_ai", 35.0f, 35.0f, 0.0, "btn_ai_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ai", 25.0, 20.0, "btn_companion_tooltip"); + + jRow = CreateButtonImage(jRow, "isk_movsilent", "btn_quiet", 35.0f, 35.0f, 0.0, "btn_quiet_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_quiet", 25.0, 20.0, "btn_quiet_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_archer", "btn_ranged", 35.0f, 35.0f, 0.0, "btn_ranged_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ranged", 25.0, 20.0, "btn_ranged_tooltip"); + + jRow = CreateButtonImage(jRow, "dm_takeitem", "btn_equip_weapon", 35.0f, 35.0f, 0.0, "btn_equip_weapon_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_equip_weapon", 25.0, 20.0, "btn_equip_weapon_tooltip"); + + jRow = CreateButtonImage(jRow, "isk_search", "btn_search", 35.0f, 35.0f, 0.0, "btn_search_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_search", 25.0, 20.0, "btn_search_tooltip"); + + jRow = CreateButtonImage(jRow, "isk_hide", "btn_stealth", 35.0f, 35.0f, 0.0, "btn_stealth_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_stealth", 25.0, 20.0, "btn_stealth_tooltip"); + + 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(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"); + + jRow = CreateButtonImage(jRow, "ir_bash", "btn_bash_locks", 35.0f, 35.0f, 0.0, "btn_bash_locks_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_bash_locks", 25.0, 20.0, "btn_bash_locks_tooltip"); + + jRow = CreateButtonImage(jRow, "dm_control", "btn_magic_level", 35.0f, 35.0f, 0.0, "btn_magic_level_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_magic_level", 25.0, 20.0, "btn_magic_level_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_xability", "btn_spontaneous", 35.0f, 35.0f, 0.0, "btn_spontaneous_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_spontaneous", 25.0, 20.0, "btn_spontaneous_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_cntrspell", "btn_magic", 35.0f, 35.0f, 0.0, "btn_magic_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_magic", 25.0, 20.0, "btn_magic_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_moreattacks", "btn_magic_items", 35.0f, 35.0f, 0.0, "btn_magic_items_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_magic_items", 25.0, 20.0, "btn_magic_items_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_orisons", "btn_def_magic", 35.0f, 35.0f, 0.0, "btn_def_magic_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_def_magic", 25.0, 20.0, "btn_def_magic_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_metamagic", "btn_off_magic", 35.0f, 35.0f, 0.0, "btn_off_magic_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_off_magic", 25.0, 20.0, "btn_off_magic_tooltip"); + + 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(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"); + + jRow = CreateButtonImage(jRow, "ir_party", "btn_healp_onoff", 35.0f, 35.0f, 0.0, "btn_healp_onoff_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_healp_onoff", 25.0, 20.0, "btn_healp_onoff_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_accept", "btn_cure_onoff", 35.0f, 35.0f, 0.0, "btn_cure_onoff_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_cure_onoff", 25.0, 20.0, "btn_cure_onoff_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_barter", "btn_loot", 35.0f, 35.0f, 0.0, "btn_loot_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_loot", 25.0, 20.0, "btn_loot_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_ignore", "btn_ignore_assoc", 35.0f, 35.0f, 0.0, "btn_ignore_assoc_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ignore_assoc", 25.0, 20.0, "btn_ignore_assoc_tooltip"); + + jRow = CreateButtonImage(jRow, "ir_abort", "btn_ignore_traps", 35.0f, 35.0f, 0.0, "btn_ignore_traps_tooltip"); + jRow = CreateCheckBox(jRow, "", "chbx_ignore_traps", 25.0, 20.0, "btn_ignore_traps_tooltip"); + + 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. + string sName = ai_RemoveIllegalCharacters(GetName(oPC)); + json jLocations = ai_GetCampaignDbJson("locations", sName, AI_DM_TABLE); + jLocations = JsonObjectGet(jLocations, "dm_widget_manager_nui"); + float fX = JsonGetFloat(JsonObjectGet(jLocations, "x")); + float fY = JsonGetFloat(JsonObjectGet(jLocations, "y")); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + sName = GetName(oPC); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int nToken = SetWindow(oPC, jLayout, "dm_widget_manager_nui", sName + " PEPS DM Widget Manager", + fX, fY, 660.0f, fHeight + 12.0f, FALSE, FALSE, TRUE, FALSE, TRUE, "0e_nui_dm"); + // Set event watches for save window location. + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + // Row 1 + NuiSetBind(oPC, nToken, "btn_check_buttons_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_check_buttons_tooltip", JsonString(" Check all buttons, removing them for all players.")); + NuiSetBind(oPC, nToken, "btn_clear_buttons_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_buttons_tooltip", JsonString(" Clear all buttons, allowing use for all players.")); + // Row 2 & 3 Labels. + // Load all the buttons states. + //int bAIWidgetLock = ai_GetDMWAccessButton(BTN_WIDGET_LOCK); + int bCmdAction = ai_GetDMWAccessButton(BTN_CMD_ACTION); + int bCmdGuard = ai_GetDMWAccessButton(BTN_CMD_GUARD); + int bCmdHold = ai_GetDMWAccessButton(BTN_CMD_HOLD); + int bCmdSearch = ai_GetDMWAccessButton(BTN_CMD_SEARCH); + int bCmdStealth = ai_GetDMWAccessButton(BTN_CMD_STEALTH); + int bCmdAttack = ai_GetDMWAccessButton(BTN_CMD_ATTACK); + int bCmdFollow = ai_GetDMWAccessButton(BTN_CMD_FOLLOW); + int bCmdAIScript = ai_GetDMWAccessButton(BTN_CMD_AI_SCRIPT); + int bCmdPlacetrap = ai_GetDMWAccessButton(BTN_CMD_PLACE_TRAP); + int bSpellWidget = ai_GetDMWAccessButton(BTN_CMD_SPELL_WIDGET); + int bMemorizeSpells = ai_GetDMWAccessButton(BTN_DM_CMD_MEMORIZE); + int bBuffShort = ai_GetDMWAccessButton(BTN_BUFF_SHORT); + int bBuffLong = ai_GetDMWAccessButton(BTN_BUFF_LONG); + int bBuffAll = ai_GetDMWAccessButton(BTN_BUFF_ALL); + int bBuffRest = ai_GetDMWAccessButton(BTN_BUFF_REST); + int bJumpTo = ai_GetDMWAccessButton(BTN_CMD_JUMP_TO); + int bGhostMode = ai_GetDMWAccessButton(BTN_CMD_GHOST_MODE); + int bCamera = ai_GetDMWAccessButton(BTN_CMD_CAMERA); + int bInventory = ai_GetDMWAccessButton(BTN_CMD_INVENTORY); + int bFamiliar = ai_GetDMWAccessButton(BTN_CMD_FAMILIAR); + int bCompanion = ai_GetDMWAccessButton(BTN_CMD_COMPANION); + int bFollowTarget = ai_GetDMAIAccessButton(BTN_AI_FOLLOW_TARGET); + int bAI = ai_GetDMAIAccessButton(BTN_AI_FOR_PC); + int bReduceSpeech = ai_GetDMAIAccessButton(BTN_AI_REDUCE_SPEECH); + int bRanged = ai_GetDMAIAccessButton(BTN_AI_USE_RANGED); + int bEquipWeapons = ai_GetDMAIAccessButton(BTN_AI_STOP_WEAPON_EQUIP); + int bSearch = ai_GetDMAIAccessButton(BTN_AI_USE_SEARCH); + int bStealth = ai_GetDMAIAccessButton(BTN_AI_USE_STEALTH); + int bOpenDoors = ai_GetDMAIAccessButton(BTN_AI_OPEN_DOORS); + int bTraps = ai_GetDMAIAccessButton(BTN_AI_REMOVE_TRAPS); + int bPickLocks = ai_GetDMAIAccessButton(BTN_AI_PICK_LOCKS); + int bBashLocks = ai_GetDMAIAccessButton(BTN_AI_BASH_LOCKS); + int bMagicLevel = ai_GetDMAIAccessButton(BTN_AI_MAGIC_LEVEL); + int bSpontaneous = ai_GetDMAIAccessButton(BTN_AI_NO_SPONTANEOUS); + int bNoMagic = ai_GetDMAIAccessButton(BTN_AI_NO_MAGIC_USE); + int bNoMagicItems = ai_GetDMAIAccessButton(BTN_AI_NO_MAGIC_ITEM_USE); + int bDefMagic = ai_GetDMAIAccessButton(BTN_AI_DEF_MAGIC_USE); + int bOffMagic = ai_GetDMAIAccessButton(BTN_AI_OFF_MAGIC_USE); + int bHealOut = ai_GetDMAIAccessButton(BTN_AI_HEAL_OUT); + int bHealIn = ai_GetDMAIAccessButton(BTN_AI_HEAL_IN); + int bSelfHealOnOff = ai_GetDMAIAccessButton(BTN_AI_STOP_SELF_HEALING); + int bPartyHealOnOff = ai_GetDMAIAccessButton(BTN_AI_STOP_PARTY_HEALING); + int bCureOnOff = ai_GetDMAIAccessButton(BTN_AI_STOP_CURE_SPELLS); + int bLoot = ai_GetDMAIAccessButton(BTN_AI_LOOT); + int bIgnoreAssociates = ai_GetDMAIAccessButton(BTN_AI_IGNORE_ASSOCIATES); + int bIgnoreTraps = ai_GetDMAIAccessButton(BTN_AI_IGNORE_TRAPS); + int bPercRange = ai_GetDMAIAccessButton(BTN_AI_PERC_RANGE); + int bBtnFamiliar = ai_GetDMWAccessButton(BTN_CMD_FAMILIAR); + int bBtnCompanion = ai_GetDMWAccessButton(BTN_CMD_COMPANION); + SetLocalInt(oPC, "CHBX_SKIP", TRUE); + DelayCommand(2.0, DeleteLocalInt(oPC, "CHBX_SKIP")); + // 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)); + NuiSetBind(oPC, nToken, "btn_cmd_action_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_action_tooltip", JsonString(" Action button")); + + NuiSetBind(oPC, nToken, "chbx_cmd_guard_check", JsonBool (bCmdGuard)); + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_guard_tooltip", JsonString(" Guard button")); + + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_hold_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_hold_tooltip", JsonString(" Hold button")); + + NuiSetBind(oPC, nToken, "chbx_cmd_attack_check", JsonBool (bCmdAttack)); + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_attack_tooltip", JsonString(" Attack button")); + + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_follow_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_follow_tooltip", JsonString(" Follow button")); + + NuiSetBind(oPC, nToken, "chbx_follow_target_check", JsonBool (bFollowTarget)); + 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)); + NuiSetBind(oPC, nToken, "btn_follow_target_tooltip", JsonString(" Follow Target button")); + + NuiSetBind(oPC, nToken, "chbx_cmd_search_check", JsonBool (bCmdSearch)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_search_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_cmd_search_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_search_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_search_tooltip", JsonString(" Search All button")); + + NuiSetBind(oPC, nToken, "chbx_cmd_stealth_check", JsonBool (bCmdStealth)); + NuiSetBindWatch (oPC, nToken, "chbx_cmd_stealth_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_cmd_stealth_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_stealth_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_stealth_tooltip", JsonString(" Stealth All button")); + + 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)); + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_cmd_ai_script_tooltip", JsonString(" Combat Tactics button")); + + 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)); + NuiSetBind(oPC, nToken, "btn_quick_widget_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_quick_widget_tooltip", JsonString(" Quick Use button")); + + NuiSetBind(oPC, nToken, "chbx_spell_memorize_check", JsonBool (bMemorizeSpells)); + NuiSetBindWatch (oPC, nToken, "chbx_spell_memorize_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_spell_memorize_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_spell_memorize_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_spell_memorize_tooltip", JsonString(" Memorize Spells button")); + + 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)); + NuiSetBind (oPC, nToken, "btn_buff_short_tooltip", JsonString(" Short Buffing button")); + + 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(" Long Buffing button")); + + 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(" All Buffing button")); + + NuiSetBind(oPC, nToken, "chbx_buff_rest_check", JsonBool (bBuffRest)); + NuiSetBindWatch (oPC, nToken, "chbx_buff_rest_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_buff_rest_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_buff_rest_event", JsonBool (TRUE)); + NuiSetBind (oPC, nToken, "btn_buff_rest_tooltip", JsonString(" Rest Buffing button")); + + 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)); + NuiSetBind(oPC, nToken, "btn_jump_to_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_jump_to_tooltip", JsonString(" Jump Associates button")); + + NuiSetBind(oPC, nToken, "chbx_ghost_mode_check", JsonBool (bGhostMode)); + NuiSetBindWatch (oPC, nToken, "chbx_ghost_mode_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ghost_mode_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ghost_mode_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_ghost_mode_tooltip", JsonString(" Ghost mode button")); + + NuiSetBind(oPC, nToken, "chbx_camera_check", JsonBool (bCamera)); + NuiSetBindWatch (oPC, nToken, "chbx_camera_check", TRUE); + 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")); + + 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)); + NuiSetBind(oPC, nToken, "btn_familiar_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_familiar_tooltip", JsonString(" Change Familiar buttons")); + + NuiSetBind(oPC, nToken, "chbx_companion_check", JsonBool(bBtnCompanion)); + NuiSetBindWatch (oPC, nToken, "chbx_companion_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_companion_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_companion_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_companion_tooltip", JsonString(" Change Animal Companion buttons")); + + NuiSetBind(oPC, nToken, "chbx_ai_check", JsonBool(bAI)); + NuiSetBindWatch (oPC, nToken, "chbx_ai_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ai_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ai_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ai_tooltip", JsonString(" Player AI button")); + + NuiSetBind(oPC, nToken, "chbx_quiet_check", JsonBool(bReduceSpeech)); + NuiSetBindWatch (oPC, nToken, "chbx_quiet_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_quiet_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_quiet_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_quiet_tooltip", JsonString(" Reduce Speech button")); + + NuiSetBind(oPC, nToken, "chbx_ranged_check", JsonBool(bRanged)); + NuiSetBindWatch(oPC, nToken, "chbx_ranged_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ranged_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ranged_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_ranged_tooltip", JsonString(" Ranged button")); + + NuiSetBind(oPC, nToken, "chbx_equip_weapon_check", JsonBool(bEquipWeapons)); + NuiSetBindWatch(oPC, nToken, "chbx_equip_weapon_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_equip_weapon_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_equip_weapon_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_equip_weapon_tooltip", JsonString(" Auto Equip Weapons button")); + + NuiSetBind(oPC, nToken, "chbx_search_check", JsonBool(bSearch)); + NuiSetBindWatch (oPC, nToken, "chbx_search_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_search_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_search_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_search_tooltip", JsonString(" Search button")); + + NuiSetBind(oPC, nToken, "chbx_stealth_check", JsonBool(bStealth)); + NuiSetBindWatch(oPC, nToken, "chbx_stealth_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_stealth_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_stealth_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_stealth_tooltip", JsonString(" Stealth button")); + + NuiSetBind(oPC, nToken, "chbx_open_door_check", JsonBool(bOpenDoors)); + NuiSetBindWatch (oPC, nToken, "chbx_open_door_check", TRUE); + 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")); + + 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)); + NuiSetBind(oPC, nToken, "btn_pick_locks_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_pick_locks_tooltip", JsonString(" Pick Locks button")); + + NuiSetBind(oPC, nToken, "chbx_bash_locks_check", JsonBool(bBashLocks)); + NuiSetBindWatch(oPC, nToken, "chbx_bash_locks_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_bash_locks_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_bash_locks_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_bash_locks_tooltip", JsonString(" Bash button")); + + NuiSetBind(oPC, nToken, "chbx_magic_level_check", JsonBool(bMagicLevel)); + NuiSetBindWatch (oPC, nToken, "chbx_magic_level_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_magic_level_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_level_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_magic_level_tooltip", JsonString(" Magic Level button")); + + NuiSetBind(oPC, nToken, "chbx_spontaneous_check", JsonBool(bSpontaneous)); + NuiSetBindWatch (oPC, nToken, "chbx_spontaneous_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_spontaneous_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_spontaneous_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_spontaneous_tooltip", JsonString(" Spontaneous Spells button")); + + NuiSetBind(oPC, nToken, "chbx_magic_check", JsonBool(bNoMagic)); + NuiSetBindWatch (oPC, nToken, "chbx_magic_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_tooltip", JsonString(" Use Magic button")); + + NuiSetBind(oPC, nToken, "chbx_magic_items_check", JsonBool(bNoMagicItems)); + NuiSetBindWatch (oPC, nToken, "chbx_magic_items_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_magic_items_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_items_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_magic_items_tooltip", JsonString(" Use Magic Items button")); + + NuiSetBind(oPC, nToken, "chbx_def_magic_check", JsonBool (bDefMagic)); + NuiSetBindWatch (oPC, nToken, "chbx_def_magic_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_def_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_def_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_def_magic_tooltip", JsonString(" Use Defensive Magic button")); + + NuiSetBind(oPC, nToken, "chbx_off_magic_check", JsonBool(bOffMagic)); + NuiSetBindWatch (oPC, nToken, "chbx_off_magic_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_off_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_off_magic_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_off_magic_tooltip", JsonString(" Use Offensive Magic button")); + + NuiSetBind(oPC, nToken, "chbx_heal_out_check", JsonBool(bHealOut)); + NuiSetBindWatch (oPC, nToken, "chbx_heal_out_check", TRUE); + 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")); + + 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)); + NuiSetBind(oPC, nToken, "btn_heals_onoff_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_heals_onoff_tooltip", JsonString(" Heal Self On/Off button")); + + NuiSetBind(oPC, nToken, "chbx_healp_onoff_check", JsonBool(bPartyHealOnOff)); + NuiSetBind(oPC, nToken, "chbx_healp_onoff_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "chbx_healp_onoff_check", TRUE); + NuiSetBind(oPC, nToken, "btn_healp_onoff_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_healp_onoff_tooltip", JsonString(" Heal Party On/Off button")); + + NuiSetBind(oPC, nToken, "chbx_cure_onoff_check", JsonBool(bCureOnOff)); + NuiSetBind(oPC, nToken, "chbx_cure_onoff_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "chbx_cure_onoff_check", TRUE); + NuiSetBind(oPC, nToken, "btn_cure_onoff_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cure_onoff_tooltip", JsonString(" Cure Spells On/Off button")); + + NuiSetBind(oPC, nToken, "chbx_loot_check", JsonBool(bLoot)); + NuiSetBindWatch (oPC, nToken, "chbx_loot_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_loot_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_loot_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_loot_tooltip", JsonString(" Auto Looting button")); + + NuiSetBind(oPC, nToken, "chbx_ignore_assoc_check", JsonBool(bIgnoreAssociates)); + NuiSetBindWatch(oPC, nToken, "chbx_ignore_assoc_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ignore_assoc_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ignore_assoc_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_ignore_assoc_tooltip", JsonString(" Ignore Associates On/Off button")); + + NuiSetBind(oPC, nToken, "chbx_ignore_traps_check", JsonBool(bIgnoreTraps)); + NuiSetBindWatch(oPC, nToken, "chbx_ignore_traps_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_ignore_traps_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_ignore_traps_event", JsonBool(TRUE)); + NuiSetBind (oPC, nToken, "btn_ignore_traps_tooltip", JsonString(" Ignore Floor Traps On/Off button")); + + NuiSetBind(oPC, nToken, "chbx_perc_range_check", JsonBool(bPercRange)); + NuiSetBindWatch (oPC, nToken, "chbx_perc_range_check", TRUE); + NuiSetBind(oPC, nToken, "chbx_perc_range_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_perc_range_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_perc_range_tooltip", JsonString(" Perception Range button")); +} + diff --git a/_module/nss/0i_messages.nss b/_module/nss/0i_messages.nss new file mode 100644 index 00000000..ff01269a --- /dev/null +++ b/_module/nss/0i_messages.nss @@ -0,0 +1,88 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_messages +////////////////////////////////////////////////////////////////////////////////////////////////////// + Include script for sending messages to files and players on the server. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_constants" +#include "0i_color" +// Sets up a Message on the module to be sent to the log and/or players. +// sTextColor color of text sent to the players and DM's. +// Use AI_COLOR_*. Where * is WHITE, RED, GREEN, BLUE, GRAY, or YELLOW. +// If nLog is TRUE it will send the message to the log file. +// If nToDMs is TRUE it will send the message to all DM's. +// If oPC is set to a player then they will get the message as well. +// Messages delivered by script should be colored as follows. +// _Debug message = COLOR_WHITE +// Generic messages for the player = AI_COLOR_YELLOW +// Negative messages for the player = AI_COLOR_RED +// Positive messages for the player = AI_COLOR_GREEN +// System messages, things that are not part of Dnd = COLOR_GRAY +// Descriptive in game messages = COLOR_BLUE +void ai_SendMessages(string sMessage, string sTextColor = AI_COLOR_YELLOW, object oPC = OBJECT_INVALID, int nToDMs = FALSE, int nLog = FALSE); +// Used for _debugging. Keeps all the information organized. +// Sends info to first pc if true and sends information to log file. +// sScriptName is the name of the script calling this function. +// sLineNumber is the line number of the code calling this function. +// sMessage is the description of the debug being sent. +void ai_Debug(string sScriptName, string sLineNumber, string sMessage); +// A counter to track microseconds in code. Start saves the counter. +void ai_Counter_Start(); +// A counter to track microseconds in code. End displays the time between Start +// and End to the log file. +void ai_Counter_End(string sMessage = ""); + +void ai_SendMessages(string sMessage, string sTextColor = AI_COLOR_YELLOW, object oPC = OBJECT_INVALID, int nToDMs = FALSE, int nLog = FALSE) +{ + // if nLog is TRUE send the message to the log file. + if(nLog) + { + sMessage = ai_StripColorCodes(sMessage); + // Add PC name to log to know who it belongs to. + string sLogPCName; + if(oPC != OBJECT_INVALID) sLogPCName = "(" + GetName(oPC) + ") "; + WriteTimestampedLogEntry("*** MESSAGE: " + sLogPCName + " " + sMessage); + } + sMessage = ai_AddColorToText(sMessage, sTextColor); + if(oPC != OBJECT_INVALID) SendMessageToPC(oPC, sMessage); + // If nToDMs is true send message to the DM's online. + if(nToDMs) SendMessageToAllDMs(sMessage); +} +void ai_Debug(string sScriptName, string sLineNumber, string sMessage) +{ + string sName = GetName(OBJECT_SELF); + if(sName == GetLocalString(GetModule(), AI_RULE_DEBUG_CREATURE) && + sName != "") + { + sMessage = "(((DEBUG)))[" + sScriptName + " - " + sLineNumber + " ]" + sMessage; + sMessage = ai_StripColorCodes(sMessage); + WriteTimestampedLogEntry(sMessage); + return; + } + //sMessage = "(((DEBUG)))[" + sScriptName + " - " + sLineNumber + " ]" + sMessage; + //sMessage = ai_StripColorCodes(sMessage); + //SendMessageToPC(GetFirstPC(), sMessage); + //WriteTimestampedLogEntry(sMessage); + //if(GetLocalInt(OBJECT_SELF, "AI_DEBUG")) WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Kirrin") WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Dorna Trapspringer") WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Dire Spider") WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Shadow Priest") WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Tomi Undergallows") WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Thello Colds") WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Gert Sigers") WriteTimestampedLogEntry(sMessage); + //if(GetName(OBJECT_SELF) == "Zombie") WriteTimestampedLogEntry(sMessage); +} +void ai_Counter_Start() +{ + SetLocalInt(GetModule(), "0_MSCounter", GetMicrosecondCounter()); +} +void ai_Counter_End(string sMessage = "") +{ + int nTime = GetMicrosecondCounter(); + nTime = nTime - GetLocalInt(GetModule(), "0_MSCounter"); + float fTime = nTime / 1000000.0; + if(AI_DEBUG) ai_Debug("MICROSECOND_COUNTER", "", "Seconds: " + FloatToString(fTime, 0, 10) + + " Microseconds: " + IntToString(nTime) + " " + sMessage); +} diff --git a/_module/nss/0i_module.nss b/_module/nss/0i_module.nss new file mode 100644 index 00000000..6d501824 --- /dev/null +++ b/_module/nss/0i_module.nss @@ -0,0 +1,544 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_module +//////////////////////////////////////////////////////////////////////////////// + Include script for handling event scripts for injecting the systems into a + module for either single player or a server. +*/////////////////////////////////////////////////////////////////////////////// +#include "x2_inc_switches" +#include "0i_associates" +#include "0i_menus" +#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. +// If oPC is passed as Invalid then it will get the firt PC in the game. +void ai_CheckPCStart(object oPC = OBJECT_INVALID); +// 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. +object ai_ChangeAssociate(object oCreature, object oModule); +// Sets the events for oCreature that is a Monster while playing Infinite Dungeons. +void ai_SetIDMonsterEventScripts(object oCreature); +// Sets the events for oCreature that is a monster in while using the PRC and +// playing Infinite Dungeons. +void ai_SetPRCIDMonsterEventScripts(object oCreature); +// Sets the events for oCreature that is an associate while using the PRC. +void ai_SetPRCAssociateEventScripts(object oCreature); +// Reverts single player monster event scripts back to their default. +void ai_ChangeEventScriptsForMonster(object oCreature); +// Reverts single player associates event scripts back to their default. +void ai_ChangeEventScriptsForAssociate(object oCreature); +// If using PRC this will replace some spells with PRC variants. +json ai_ReplaceSpellsWithPRCVariants(object oCreature, json jCreature); + +//****************************************************************************** +//********************* Creature event scripts ********************************* +//****************************************************************************** +int ai_OnMonsterSpawn(object oCreature) +{ + if(GetLocalInt(oCreature, AI_ONSPAWN_EVENT)) return FALSE; + SetLocalInt(oCreature, AI_ONSPAWN_EVENT, TRUE); + object oModule = GetModule(); + int nInfiniteDungeons; + int nPRC = GetLocalInt(oModule, AI_USING_PRC); + // If you are running a server this will not affect the module. + if(!AI_SERVER) + { + ai_CheckPCStart(); + string sModuleName = GetModuleName(); + if(sModuleName == "Neverwinter Nights - Infinite Dungeons" || + sModuleName == "Infinite Dungeons [PRC8]") + { + nInfiniteDungeons = TRUE; + if(nPRC) ai_SetPRCIDMonsterEventScripts(oCreature); + else ai_SetIDMonsterEventScripts(oCreature); + // Fix to get plot givers, finishers from getting killed a lot. + if(GetLocalString(oCreature, "sConversation") == "id1_plotgiver " || + GetLocalString(oCreature, "sConversation") == "id1_plotdest") + { + ChangeToStandardFaction(oCreature, STANDARD_FACTION_MERCHANT); + SetStandardFactionReputation(STANDARD_FACTION_HOSTILE, 50, oCreature); + } + } + } + // PRC and Infinite dungeons has issues with Ondeath script so we just leave it alone. + if(!nPRC && !nInfiniteDungeons) + { + // We change this script so we can setup permanent summons on/off. + string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH); + SetLocalString(oCreature, "AI_ON_DEATH", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH, "0e_c2_7_ondeath"); + } + if(GetCreatureFlag(oCreature, CREATURE_VAR_IS_INCORPOREAL)) + { + string sCombatAI = GetLocalString(oCreature, AI_DEFAULT_SCRIPT); + if (sCombatAI == "") SetLocalString(oCreature, AI_DEFAULT_SCRIPT, "ai_incorporeal"); + } + ai_SetListeningPatterns(oCreature); + ai_SetCreatureAIScript(oCreature); + ai_SetNormalAppearance(oCreature); + ai_SetAura(oCreature); + SetLocalInt(oCreature, AI_HEAL_IN_COMBAT_LIMIT, AI_MONSTER_HEAL_IN_COMBAT_CHANCE); + SetLocalInt(oCreature, AI_HEAL_OUT_OF_COMBAT_LIMIT, AI_MONSTER_HEAL_OUT_COMBAT_CHANCE); + int nMonsterHpIncrease = GetLocalInt(oModule, AI_INCREASE_MONSTERS_HP); + if(nMonsterHpIncrease) + { + int nHp = GetMaxHitPoints(oCreature); + nHp = (nHp * nMonsterHpIncrease) / 100; + effect eHp = EffectTemporaryHitpoints(nHp); + eHp = SupernaturalEffect(eHp); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eHp, oCreature); + } + // Check if the monster should change how they search for targets. + if(d100() <= GetLocalInt(GetModule(), AI_RULE_AI_DIFFICULTY)) + { + SetLocalInt(oCreature, AI_RULE_AI_DIFFICULTY, TRUE); + } + // Do json changes after we have setup the creature. + if(ai_ChangeMonster(oCreature, oModule)) return TRUE; + return FALSE; +} +void ai_OnAssociateSpawn(object oCreature) +{ + if(GetLocalInt(oCreature, AI_ONSPAWN_EVENT)) return; + 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(bPRC) ai_SetPRCAssociateEventScripts(oCreature); + } + // PRC has issues with Ondeath script so we just leave it alone. + if(!bPRC) + { + // 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"); + } + // Initialize Associate modes for basic use. + ai_SetListeningPatterns(oCreature); + ai_SetNormalAppearance(oCreature); + ai_SetAssociateAIScript(oCreature, FALSE); + ai_SetAura(oCreature); + if(GetLocalInt(GetModule(), AI_RULE_PARTY_SCALE)) ai_CheckXPPartyScale(oCreature); + // Bioware summoned shadows are not incorporeal, also set the ai code. + if (GetTag(OBJECT_SELF) == "NW_S_SHADOW") + { + SetLocalInt(OBJECT_SELF, "X2_L_IS_INCORPOREAL", TRUE); + SetLocalString(OBJECT_SELF, AI_DEFAULT_SCRIPT, "ai_shadow"); + } +} +void ai_CheckPCStart(object oPC = OBJECT_INVALID) +{ + if(oPC == OBJECT_INVALID) oPC = GetFirstPC(); + // There should always be a PC widget. If it doesn't exist then we assume + // that the module is being loaded or started. + if(!NuiFindWindow(oPC, "pc" + 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_CheckAssociateData(oPC, oPC, "pc"); + ai_StartupPlugins(oPC); + ai_SetupPlayerTarget(oPC); + ai_SetupModuleGUIEvents(oPC); + ai_CreateWidgetNUI(oPC, oPC); + ai_SetNormalAppearance(oPC); + } +} +void ai_CopyMonster(object oCreature, object oModule) +{ + // After setting the monster lets see if we should copy it. + float fMonsterIncrease = GetLocalFloat(oModule, AI_INCREASE_ENC_MONSTERS); + if(GetIsEncounterCreature(oCreature) && fMonsterIncrease > 0.0) + { + int nMonsterIncrease; + float fMonsterCounter = GetLocalFloat(oModule, "AI_MONSTER_COUNTER"); + fMonsterCounter += fMonsterIncrease; + nMonsterIncrease = FloatToInt(fMonsterCounter); + if(nMonsterIncrease > 0) + { + fMonsterCounter = fMonsterCounter - IntToFloat(nMonsterIncrease); + } + SetLocalFloat(oModule, "AI_MONSTER_COUNTER", fMonsterCounter); + while(nMonsterIncrease > 0) + { + CopyObject(oCreature, GetLocation(oCreature), OBJECT_INVALID, "", TRUE); + nMonsterIncrease = nMonsterIncrease - 1; + } + } +} +void ai_CreateMonster(json jCreature, location lLocation, object oModule) +{ + //WriteTimestampedLogEntry("0i_module, 181, " + JsonDump(jCreature, 1)); + object oCreature = JsonToObject(jCreature, lLocation, OBJECT_INVALID, TRUE); + // 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)) + { + SetIsDestroyable(FALSE, FALSE, TRUE, oCreature); + SetLootable(oCreature, TRUE); + } */ + if(AI_DEBUG) ai_Debug("0i_module", "187", GetName(oCreature)); + ai_CopyMonster(oCreature, oModule); + return; +} +json ai_SetCompanionSummoning(object oCreature, json jCreature) +{ + if(GetHasFeat(FEAT_SUMMON_FAMILIAR, oCreature, TRUE)) + { + json jFamiliar = JsonObjectGet(jCreature, "FamiliarName"); + jFamiliar = JsonObjectSet(jFamiliar, "value", JsonString("Summoned Familiar")); + jCreature = JsonObjectSet(jCreature, "FamiliarName", jFamiliar); + jFamiliar = JsonObjectGet(jCreature, "FamiliarType"); + jFamiliar = JsonObjectSet(jFamiliar, "value", JsonInt(Random(11))); + return JsonObjectSet(jCreature, "FamiliarType", jFamiliar); + } + if(GetHasFeat(FEAT_ANIMAL_COMPANION , oCreature, TRUE)) + { + json jCompanion = JsonObjectGet(jCreature, "CompanionName"); + jCompanion = JsonObjectSet(jCompanion, "value", JsonString("Summoned Companion")); + jCreature = JsonObjectSet(jCreature, "CompanionName", jCompanion); + jCompanion = JsonObjectGet(jCreature, "CompanionType"); + jCompanion = JsonObjectSet(jCompanion, "value", JsonInt(Random(9))); + return JsonObjectSet(jCreature, "CompanionType", jCompanion); + } + return jCreature; +} +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); + // 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(IsInConversation(oCreature)) return FALSE; + json jCreature = ObjectToJson(oCreature, TRUE); + // We now use plugins to mod our monsters. + json jMonsterMods = GetLocalJson(oModule, AI_MONSTER_MOD_JSON); + if(JsonGetType(jMonsterMods) != JSON_TYPE_NULL) + { + SetLocalJson(oModule, AI_MONSTER_JSON, jCreature); + SetLocalObject(oModule, AI_MONSTER_OBJECT, oCreature); + int nIndex; + string sMonsterMod = JsonGetString(JsonArrayGet(jMonsterMods, nIndex)); + while(sMonsterMod != "") + { + ExecuteScript(sMonsterMod, oPC); + sMonsterMod = JsonGetString(JsonArrayGet(jMonsterMods, ++nIndex)); + } + 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); + 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) + + // " nPercDist: " + IntToString(nPercDist) + " Reputation: " + IntToString(GetReputation(oCreature, oPC))); + if(nSummon || nPercDist) + { + location lLocation = GetLocation(oCreature); + if(nPercDist) + { + json jPerception = JsonObjectGet(jCreature, "PerceptionRange"); + jPerception = JsonObjectSet(jPerception, "value", JsonInt(GetLocalInt(oModule, AI_RULE_MON_PERC_DISTANCE))); + jCreature = JsonObjectSet(jCreature, "PerceptionRange", jPerception); + } + if(nSummon) jCreature = ai_SetCompanionSummoning(oCreature, jCreature); + SetLocalInt(oModule, AI_MONSTER_CHANGED, TRUE); + } + if(GetLocalInt(oModule, AI_MONSTER_CHANGED)) + { + SetIsDestroyable(TRUE, FALSE, FALSE, oCreature); + location lLocation = GetLocation(oCreature); + DestroyObject(oCreature); + AssignCommand(oModule, DelayCommand(1.0, ai_CreateMonster(jCreature, lLocation, oModule))); + DeleteLocalInt(oModule, AI_MONSTER_CHANGED); + return TRUE; + } + else ai_CopyMonster(oCreature, oModule); + DeleteLocalJson(oModule, AI_MONSTER_JSON); + DeleteLocalObject(oModule, AI_MONSTER_OBJECT); + return FALSE; +} +// Special event scripts for Infinite Dungeons! +void ai_SetIDMonsterEventScripts(object oCreature) +{ + //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); + SetLocalString(oCreature, "AI_ON_HEARTBEAT", sScript); + if(sScript == "x2_def_heartbeat") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else if(sScript == "nw_c2_default1") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_HEARTBEAT SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Perception ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE); + SetLocalString(oCreature, "AI_ON_NOTICE", sScript); + if(sScript == "x2_def_percept") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "0e_id_events"); + else if(sScript == "nw_c2_default2") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_NOTICE SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On End Combat Round ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND); + SetLocalString(oCreature, "AI_ON_END_COMBATROUND", sScript); + if(sScript == "x2_def_endcombat") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "0e_id_events"); + else if(sScript == "nw_c2_default3") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_END_COMBATROUND SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Dialogue ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE); + SetLocalString(oCreature, "AI_ON_DIALOGUE", sScript); + if(sScript == "x2_def_onconv") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "0e_id_events"); + else if(sScript == "nw_c2_default4") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_DIALOGUE_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Melee Attacked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED); + SetLocalString(oCreature, "AI_ON_MELEE_ATTACKED", sScript); + if(sScript == "x2_def_attacked") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "0e_id_events"); + else if(sScript == "nw_c2_default5") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_MELEE_ATTACKED_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Damaged ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED); + SetLocalString(oCreature, "AI_ON_DAMAGED", sScript); + if(sScript == "x2_def_ondamage") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "0e_id_events"); + else if(sScript == "nw_c2_default6") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_DAMAGED_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + // We don't set OnDeath for Infinite Dungeons! + //********** On Death ********** + //sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH); + //SetLocalString(oCreature, "AI_ON_DEATH", sScript); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH, "0e_id_events"); + //********** On Disturbed ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED); + SetLocalString(oCreature, "AI_ON_DISTURBED", sScript); + if(sScript == "x2_def_ondisturb") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "0e_id_events"); + else if(sScript == "nw_c2_default8") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_DISTURBED_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //********** On Rested ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED); + SetLocalString(oCreature, "AI_ON_RESTED", sScript); + if(sScript == "x2_def_rested") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "0e_id_events"); + else if(sScript == "nw_c2_defaulta") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_RESTED SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Spell Cast At ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT); + SetLocalString(oCreature, "AI_ON_SPELLCASTAT", sScript); + if(sScript == "x2_def_spellcast") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "0e_id_events"); + else if(sScript == "nw_c2_defaultb") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_SPELLCASTAT_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Blocked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR); + SetLocalString(oCreature, "AI_ON_BLOCKED_BY_DOOR", sScript); + if(sScript == "x2_def_onblocked") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "0e_id_events"); + else if(sScript == "nw_c2_defaulte") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "0e_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_BLOCKED_BY_DOOR SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); +} +// Special event scripts for Infinite Dungeons with PRC! +void ai_SetPRCIDMonsterEventScripts(object oCreature) +{ + //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); + SetLocalString(oCreature, "AI_ON_HEARTBEAT", sScript); + if(sScript == "x2_def_heartbeat") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_prc_id_events"); + else if(sScript == "nw_c2_default1") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_HEARTBEAT SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Perception ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE); + SetLocalString(oCreature, "AI_ON_NOTICE", sScript); + if(sScript == "x2_def_percept") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "0e_prc_id_events"); + else if(sScript == "nw_c2_default2") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_NOTICE SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On End Combat Round ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND); + SetLocalString(oCreature, "AI_ON_END_COMBATROUND", sScript); + if(sScript == "x2_def_endcombat") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "0e_prc_id_events"); + else if(sScript == "nw_c2_default3") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_END_COMBATROUND SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Dialogue ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE); + SetLocalString(oCreature, "AI_ON_DIALOGUE", sScript); + if(sScript == "x2_def_onconv") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "0e_prc_id_events"); + else if(sScript == "nw_c2_default4") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_DIALOGUE_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Melee Attacked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED); + SetLocalString(oCreature, "AI_ON_MELEE_ATTACKED", sScript); + if(sScript == "x2_def_attacked") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "0e_prc_id_events"); + else if(sScript == "nw_c2_default5") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_MELEE_ATTACKED_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Damaged ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED); + SetLocalString(oCreature, "AI_ON_DAMAGED", sScript); + if(sScript == "x2_def_ondamage") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "0e_prc_id_events"); + else if(sScript == "nw_c2_default6") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_DAMAGED_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + // We don't set OnDeath for PRC or Infinite dungeons. + //********** On Death ********** + //sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH); + //SetLocalString(oCreature, "AI_ON_DEATH", sScript); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH, "0e_prc_id_events"); + //********** On Disturbed ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED); + SetLocalString(oCreature, "AI_ON_DISTURBED", sScript); + if(sScript == "x2_def_ondisturb") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "0e_prc_id_events"); + else if(sScript == "nw_c2_default8") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_DISTURBED_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //********** On Rested ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED); + SetLocalString(oCreature, "AI_ON_RESTED", sScript); + if(sScript == "x2_def_rested") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "0e_prc_id_events"); + else if(sScript == "nw_c2_defaulta") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_RESTED SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Spell Cast At ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT); + SetLocalString(oCreature, "AI_ON_SPELLCASTAT", sScript); + if(sScript == "x2_def_spellcast") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "0e_prc_id_events"); + else if(sScript == "nw_c2_defaultb") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_SPELLCASTAT_SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //********** On Blocked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR); + SetLocalString(oCreature, "AI_ON_BLOCKED_BY_DOOR", sScript); + if(sScript == "x2_def_onblocked") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "0e_prc_id_events"); + else if(sScript == "nw_c2_defaulte") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "0e_prc_id_events"); + else if(sScript == "") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_id_events"); + else WriteTimestampedLogEntry("ON_BLOCKED_BY_DOOR SCRIPT ERROR: AI did not capture " + sScript + " script for " + GetName(oCreature) + "."); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); +} +// Special event scripts for PRC associates! +void ai_SetPRCAssociateEventScripts(object oCreature) +{ + //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); + SetLocalString(oCreature, "AI_ON_HEARTBEAT", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ac1") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "0e_prc_ch_events"); + //********** On Perception ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE); + SetLocalString(oCreature, "AI_ON_NOTICE", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ac2") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "0e_prc_ch_events"); + //********** On End Combat Round ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND); + SetLocalString(oCreature, "AI_ON_END_COMBATROUND", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ac3") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "0e_prc_ch_events"); + //********** On Dialogue ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE); + SetLocalString(oCreature, "AI_ON_DIALOGUE", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ac4") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "0e_prc_ch_events"); + //********** On Melee Attacked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED); + SetLocalString(oCreature, "AI_ON_MELEE_ATTACKED", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ac5") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "0e_prc_ch_events"); + //********** On Damaged ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED); + SetLocalString(oCreature, "AI_ON_DAMAGED", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ac6") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "0e_prc_ch_events"); + //********** On Disturbed ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED); + SetLocalString(oCreature, "AI_ON_DISTURBED", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ac8") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "0e_prc_ch_events"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //********** On Rested ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED); + SetLocalString(oCreature, "AI_ON_RESTED", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "0e_prc_ch_events"); + else if(sScript == "nw_ch_aca") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "0e_prc_ch_events"); + //********** On Spell Cast At ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT); + SetLocalString(oCreature, "AI_ON_SPELLCASTAT", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "0e_prc_ch_events"); + else if(sScript == "nw_ch_acb") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "0e_prc_ch_events"); + //********** On Blocked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR); + SetLocalString(oCreature, "AI_ON_BLOCKED_BY_DOOR", sScript); + if(sScript == "default") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "0e_prc_ch_events"); + else if(sScript == "nw_ch_ace") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "0e_prc_ch_events"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); + if(!GetCommandable(oCreature)) SetCommandable(TRUE, oCreature); +} +void ai_ChangeEventScriptsForMonster(object oCreature) +{ + //********** 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"); + //********** On Perception ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE); + if(sScript == "0e_c2_2_percept") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "nw_c2_default2"); + //********** On End Combat Round ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND); + if(sScript == "0e_c2_3_endround") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "nw_c2_default3"); + //********** On Dialogue ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE); + if(sScript == "0e_c2_4_convers") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "nw_c2_default4"); + //********** On Melee Attacked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED); + if(sScript == "0e_c2_5_phyatked") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "nw_c2_default5"); + //********** On Damaged ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED); + if(sScript == "0e_c2_6_damaged") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "nw_c2_default6"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH, "nw_c2_deafult7"); + //********** On Disturbed ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED); + if(sScript == "0e_c2_8_disturb") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "nw_c2_default8"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "nw_c2_defaulta"); + //********** On Spell Cast At ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT); + if(sScript == "0e_c2_b_castat") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "nw_c2_defaultb"); + //********** On Blocked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR); + if(sScript == "0e_c2_e_blocked") SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "nw_c2_defaulte"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, "nw_c2_defaulte"); +} +void ai_ChangeEventScriptsForAssociate(object oCreature) +{ + 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"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "nw_ch_ac4"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "nw_ch_ac5"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "nw_ch_ac6"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DEATH, "nw_ch_ac7"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "nw_ch_ac8"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "nw_ch_aca"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "nw_ch_acb"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "nw_ch_ace"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, "nw_ch_acd"); +} diff --git a/_module/nss/0i_nui.nss b/_module/nss/0i_nui.nss new file mode 100644 index 00000000..eab5ee7c --- /dev/null +++ b/_module/nss/0i_nui.nss @@ -0,0 +1,434 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_nui +//////////////////////////////////////////////////////////////////////////////// + Include script for handling window displays. + + Use the following to get/set window information. + string sBind = NuiGetNthBind (oPlayer, nToken, FALSE, #); + json jMenuInfo = NuiGetBind (oPlayer, nToken, sBind); + # Gets json information for window : + 0 - string - "window_title" + 1 - json - "window_geometry" : "h", "w", "x", "y" + 2 - bool - "window_resizable" + 3 - bool - "window_closable" + 4 - bool - "window_transparent" + 5 - bool - "window_border" + + Layout pixel sizes: + Pixel height Title bar 33. + Pixel height Top border 10, between widgets 8, bottom border 10. + Pixel width Left border 10, between widgets 4, right border 10. + + Group outer lines add 12 to the vertical and horizontal lines. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_main" +#include "nw_inc_nui" +struct stComboBox +{ + json jIndex; + json jCombo; + json jRow; + json jResRefArray; + json jWinArray; + json jCanSummon; // Index of all the summons in summons.2da +}; + +// Saves the menu to the campaign database. +void SaveMenuToCampaignDb(object oPC, int nToken, string sWndId); +// Returns the middle of the screen for the x position. +// oPC using the menu. +// fMenuWidth - the width of the menu to display. +float GetGUIWidthMiddle(object oPC, float fMenuWidth); + +// Returns the middle of the screen for the y position. +// oPC using the menu. +// fMenuHeight - the height of the menu to display. +float GetGUIHeightMiddle(object oPC, float fMenuHeight); + +// Checks to see if sWndId is open. +// If the window is open it removes it and returns FALSE +// If the window is closed it returns TRUE +int IsWindowClosed(object oPC, string sWndId); + +// Returns the Window ID (nToken). +// oPC is the PC using the menu. +// jLayout is the Layout of the menu. +// sWinID is the string ID for this window. +// sTitle is the Title of the menu. +// fX is the X position of the menu (-1.0: Centers, -2.0: UpperRight on Mouse, -3.0: Centers top of mouse). +// fY is the Y position of the menu (-1.0: Centers, -2.0: UpperRight on Mouse, -3.0: Centers top of mouse). +// fWidth is the width of the menu. +// fHeight is the height of the menu. +// bResize - TRUE will all it to be resized. +// bCollapse - TRUE will allow the window to be collapsable. +// bClose - TRUE will allow the window to be closed. +// bTransparent - TRUE makes the menu transparent. +// bBorder - TRUE makes the menu have a border. +// sEventScript will fire this event script for this window. +int SetWindow(object oPC, json jLayout, string sWinID, string sTitle, float fX, float fY, float fWidth, float fHeight, int bResize, int bCollapse, int bClose, int bTransparent, int bBorder, string sEventScript = ""); + +// Creates a label element in jRow. +// jRow is the row the label goes into. +// sLabel is the text placed in the label. +// If "" is passed then it will create a bind of sId + "_label". +// fWidth is the width of the label. +// fHeight is the Height of the label. +// nHAlign is horizonal align [NUI_HALING_*]. +// nVAlign is vertial align [NUI_VALING_*]. +// sId is the bind the event uses sId + "_event". +// sTooltip is the tooltip bind value. +json CreateLabel(json jRow, string sLabel, string sId, float fWidth, float fHeight, int nHAlign = 0, int nVAlign = 0, float fMargin = -1.0, string sTooltip = ""); + +// Creates a basic button element in jRow. +// jRow is the row the label goes into. +// sLabel is the text placed in the button. If "" is passed then it will +// create a bind of sId + "_label". +// sId is the binds for the button and the event uses sId + "_event". +// fWidth is the width of the button. +// fHeight is the Height of the button. +// fMargin is the space around the button. +// sTooltip is the tooltip bind value. +json CreateButton(json jRow, string sLabel, string sId, float fWidth, float fHeight, float fMargin = -1.0, string sTooltip = ""); + +// Creates a basic button select element in jRow. +// jRow is the row the label goes into. +// sLabel is the text placed in the button. If "" is passed then it will +// create a bind of sId + "_label". +// sId is the binds for the button and the event uses sId + "_event". +// fWidth is the width of the button. +// fHeight is the Height of the button. +// sTooltip is the tooltip bind value. +json CreateButtonSelect(json jRow, string sLabel, string sId, float fWidth, float fHeight, string sToolTip = ""); + +// Creates a button element with an image in jRow. +// jRow is the row the label goes into. +// sImage is the resref of the image to use. +// If "" is passed then it will create a bind of sId + "_image". +// sId is the binds for the button and the event uses sId + "_event". +// fWidth is the width of the button. +// fHeight is the Height of the button. +// fMargin is the space around the button. +// sTooltip is the tooltip bind value. +json CreateButtonImage(json jRow, string sResRef, string sId, float fWidth, float fHeight, float fMargin = -1.0, string sTooltip = ""); + +// Creates a basic text box that is not editable element in jRow. +// jRow is the row the TextEdit box goes into. +// sId is the bind variable so we can change the text. +// fWidth the width of the box. +// fHeight the height of the box. +// bBorder will add border TRUE or remove it FALSE. +// nScroll use NUI_SCROLLBARS_* +// sTooltip is the tooltip bind value. +json CreateTextBox(json jRow, string sId, float fWidth, float fHeight, int bBorder = TRUE, int nScroll = NUI_SCROLLBARS_AUTO, string sTooltip = ""); + +// Creates a basic text edit box element in jRow. +// jRow is the row the TextEdit box goes into. +// sPlaceHolderBind is the bind for Placeholder. +// sValueBind is the bind variable so we can change the text. +// nMaxLength is the maximum lenght of the text (1 - 65535) +// bMultiline - True or False that is has multiple lines. +// fWidth the width of the box. +// fHeight the height of the box. +// sTooltip is the tooltip bind value. +json CreateTextEditBox(json jRow, string sPlaceHolderBind, string sValueBind, int nMaxLength, int bMultiline, float fWidth, float fHeight, string sToolTip = ""); + +// Creates a combo box element in jRow. +// jRow is the row the combo goes into. +// jCombo is the elements/list for the combo box. Use NuiComboEntry to add. +// sId is the binds for the combo and the event uses sId + "_event" +// sId + "_selected" is the bind for the selection in the combo box. +// fWidth is the width of the combo. +// fHeight is the Height of the combo. +// sTooltip is the tooltip bind value. +json CreateCombo(json jRow, json jCombo, string sId, float fWidth, float fHeight, string sToolTip = ""); + +// Creates an image element in jRow. +// jRow is the row the Image goes into. +// sImage is the resref of the image to use. If "" is passed then it will +// create a bind of sId + "_image". +// nAspect is the aspect of the image NUI_ASPECT_*. +// nHAlign is the horizontal alignment of the image NUI_HALIGN_*. +// nVAlign is the vertical alignment of the image NUI_VALIGN_*. +// fWidth the width of the box. +// fHeight the height of the box. +// sTooltip is the tooltip bind value. +json CreateImage(json jRow, string sResRef, string sId, int nAspect, int nHAlign, int nVAlign, float fWidth, float fHeight, float fMargin = -1.0, string sToolTip = ""); + +// Creates a check box element in jRow. +// jRow is the row the Checkbox box goes into. +// sLabel is the text placed in the label. +// If "" is passed then it will create a bind of sId + "_label". +// sId is the bind variable so we can change the text. +// sId + "_check" is the Bind:bool for if it is checked or not. +// fWidth is the width of the label. +// fHeight is the Height of the label. +// sTooltip is the tooltip bind value. +json CreateCheckBox(json jRow, string sLabel, string sId, float fWidth, float fHeight, string sToolTip = ""); + +// Creates a slider (Int based) element in jRow +// jRow is the row the Check box goes into. +// sId is the bind name. +// The binds are as follows. +// Value: sId + "_value" +// Minimum: sId + "_min" +// Maximum: sId + "_max" +// Step size: sId + "_stepsize" +// fWidth is the width of the slider. +// fHeight is the Height of the slider. +// sTooltip is the tooltip bind value. +json CreateSlider(json jRow, string sId, float fWidth, float fHeight, string sToolTip = ""); + +// Creates an Options element in jRow. +// jRow is the row the Options will start on. +// sId is the bind name. +// The binds are as follows: +// Value: sId + "_value" +// Event is sId + "_event" +// nDirection: NUI_DIRECTION_* +// fWidth is the width of the options labels. +// fHeight is the height of the options labels. +// sTooltip is the tooltip bind value. +json CreateOptions(json jRow, string sId, int nDirection, json jLabels, float fWidth, float fHeight, string sToolTip = ""); + +// Creates a list element in jRow. +// jRow is the row the list will start on. +// jElements is the list of elements in the list. Use NuiListTemplateCell to add. +// sId is the bind name. +// The binds are Event is sId + "_event". +// Row count is bound to sId + "_count". +// fRowHeight is the height of the rendered rows. +// fWidth is the width of the options labels. +// fHeight is the height of the options labels. +// sTooltip is the tooltip bind value. +json CreateList(json jRow, json jElements, string sId, float fRowHeight, float fWidth, float fHeight, string sTooltip = ""); + +// Placed here temporarily until we can clean up our includes! +void ai_SetDMWAccessButton(int nButton, int bOn = TRUE); +int ai_GetDMWAccessButton(int nButton); +void ai_SetDMAIAccessButton(int nButton, int bOn = TRUE); +int ai_GetDMAIAccessButton(int nButton); + +void SaveMenuToCampaignDb(object oPC, int nToken, string sWndId) +{ + json jGeometry = NuiGetBind(oPC, nToken, "window_geometry"); + float fX = JsonGetFloat(JsonObjectGet(jGeometry, "x")); + float fY = JsonGetFloat(JsonObjectGet(jGeometry, "y")); + string sName = ai_RemoveIllegalCharacters(GetName(oPC)); + json jLocations = ai_GetCampaignDbJson("locations", sName, AI_DM_TABLE); + json jNUI = JsonObjectGet(jLocations, sWndId); + if(JsonGetType(jNUI) == JSON_TYPE_NULL) jNUI = JsonObject(); + jNUI = JsonObjectSet(jNUI, "x", JsonFloat(fX)); + jNUI = JsonObjectSet(jNUI, "y", JsonFloat(fY)); + jLocations = JsonObjectSet(jLocations, sWndId, jNUI); + ai_SetCampaignDbJson("locations", jLocations, sName, AI_DM_TABLE); +} + +float GetGUIWidthMiddle(object oPC, float fMenuWidth) +{ + // Get players window information. + float fGUI_Width = IntToFloat(GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_WIDTH)); + float fGUI_Scale = IntToFloat(GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_SCALE)) / 100.0; + fMenuWidth = fMenuWidth * fGUI_Scale; + return (fGUI_Width / 2.0) - (fMenuWidth / 2.0); +} +float GetGUIHeightMiddle(object oPC, float fMenuHeight) +{ + // Get players window information. + float fGUI_Height = IntToFloat(GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_HEIGHT)); + float fGUI_Scale = IntToFloat(GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_SCALE)) / 100.0; + fMenuHeight = fMenuHeight * fGUI_Scale; + return (fGUI_Height / 2.0) - (fMenuHeight / 2.0); +} +int IsWindowClosed(object oPC, string sWndId) +{ + int nToken = NuiFindWindow(oPC, sWndId); + if(nToken) + { + NuiDestroy(oPC, nToken); + return FALSE; + } + return TRUE; +} +int SetWindow(object oPC, json jLayout, string sWinID, string sTitle, float fX, float fY, float fWidth, float fHeight, int bResize, int bCollapse, int bClose, int bTransparent, int bBorder, string sEventScript = "") +{ + json jWindow; + if (bCollapse) jWindow = NuiWindow (jLayout, NuiBind ("window_title"), NuiBind ("window_geometry"), + NuiBind ("window_resizable"), JsonNull (), NuiBind ("window_closable"), + NuiBind ("window_transparent"), NuiBind ("window_border")); + + else jWindow = NuiWindow (jLayout, NuiBind ("window_title"), NuiBind ("window_geometry"), + NuiBind ("window_resizable"), JsonBool (FALSE), NuiBind ("window_closable"), + NuiBind ("window_transparent"), NuiBind ("window_border")); + + int nToken = NuiCreate (oPC, jWindow, sWinID, sEventScript); + if(!bCollapse && !bClose && !bBorder) NuiSetBind (oPC, nToken, "window_title", JsonBool (FALSE)); + else NuiSetBind (oPC, nToken, "window_title", JsonString (sTitle)); + if (fX == -1.0) fX = GetGUIWidthMiddle (oPC, fWidth); + if (fY == -1.0) fY = GetGUIHeightMiddle (oPC, fHeight); + int nScale = GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_SCALE); + if(nScale != 100) + { + fHeight = fHeight * (IntToFloat(1050 - nScale) / 1000.0); + fWidth = fWidth * (IntToFloat(1200 - nScale) / 1000.0); + } + NuiSetBind (oPC, nToken, "window_geometry", NuiRect (fX, + fY, fWidth, fHeight)); + NuiSetBind (oPC, nToken, "window_resizable", JsonBool (bResize)); + NuiSetBind (oPC, nToken, "window_closable", JsonBool (bClose)); + NuiSetBind (oPC, nToken, "window_transparent", JsonBool (bTransparent)); + NuiSetBind (oPC, nToken, "window_border", JsonBool (bBorder)); + return nToken; +} +json CreateLabel(json jRow, string sLabel, string sId, float fWidth, float fHeight, int nHAlign = 0, int nVAlign = 0, float fMargin = -1.0, string sTooltip = "") +{ + json jLabel; + if(sLabel == "") jLabel = NuiId(NuiLabel(NuiBind(sId + "_label"), JsonInt(nHAlign), JsonInt(nVAlign)), sId); + else jLabel = NuiId(NuiLabel(JsonString(sLabel), JsonInt(nHAlign), JsonInt(nVAlign)), sId); + jLabel = NuiWidth(jLabel, fWidth); + jLabel = NuiHeight(jLabel, fHeight); + if (fMargin > -1.0) jLabel = NuiMargin(jLabel, fMargin); + if(sTooltip != "") jLabel = NuiTooltip (jLabel, NuiBind (sTooltip)); + return JsonArrayInsert(jRow, jLabel); +} +json CreateButton(json jRow, string sLabel, string sId, float fWidth, float fHeight, float fMargin = -1.0, string sTooltip = "") +{ + json jButton; + if(sLabel == "") jButton = NuiEnabled(NuiId(NuiButton(NuiBind (sId + "_label")), sId), NuiBind(sId + "_event")); + else jButton = NuiEnabled(NuiId(NuiButton(JsonString(sLabel)), sId), NuiBind(sId + "_event")); + jButton = NuiWidth(jButton, fWidth); + jButton = NuiHeight(jButton, fHeight); + if (fMargin > -1.0) jButton = NuiMargin(jButton, fMargin); + if (sTooltip != "") jButton = NuiTooltip(jButton, NuiBind (sTooltip)); + return JsonArrayInsert(jRow, jButton); +} +json CreateButtonSelect(json jRow, string sLabel, string sId, float fWidth, float fHeight, string sTooltip = "") +{ + json jButton; + if(sLabel == "") jButton = NuiEnabled(NuiId(NuiButtonSelect(NuiBind (sId + "_label"), NuiBind(sId)), sId), NuiBind(sId + "_event")); + else jButton = NuiEnabled(NuiId(NuiButtonSelect(JsonString(sLabel), NuiBind(sId)), sId), NuiBind(sId + "_event")); + jButton = NuiWidth(jButton, fWidth); + jButton = NuiHeight(jButton, fHeight); + if(sTooltip != "") jButton = NuiTooltip(jButton, NuiBind (sTooltip)); + return JsonArrayInsert(jRow, jButton); +} +json CreateButtonImage(json jRow, string sResRef, string sId, float fWidth, float fHeight, float fMargin = -1.0, string sTooltip = "") +{ + json jButton; + if(sResRef == "") jButton = NuiEnabled(NuiId (NuiButtonImage(NuiBind(sId + "_image")), sId), NuiBind(sId + "_event")); + else jButton = NuiEnabled(NuiId(NuiButtonImage(JsonString(sResRef)), sId), NuiBind(sId + "_event")); + jButton = NuiWidth(jButton, fWidth); + jButton = NuiHeight(jButton, fHeight); + if(fMargin > -1.0) jButton = NuiMargin(jButton, fMargin); + if(sTooltip != "") jButton = NuiTooltip(jButton, NuiBind (sTooltip)); + jButton = NuiEncouraged(jButton, NuiBind(sId + "_encouraged")); + return JsonArrayInsert(jRow, jButton); +} +json CreateTextBox(json jRow, string sId, float fWidth, float fHeight, int bBorder = TRUE, int nScroll = NUI_SCROLLBARS_AUTO, string sTooltip = "") +{ + json jTextBox = NuiEnabled(NuiText(NuiBind(sId), bBorder, nScroll), NuiBind(sId + "_event")); + jTextBox = NuiWidth(jTextBox, fWidth); + jTextBox = NuiHeight(jTextBox, fHeight); + if(sTooltip != "") jTextBox = NuiTooltip(jTextBox, NuiBind (sTooltip)); + return JsonArrayInsert(jRow, JsonObjectSet(jTextBox, "text_color", NuiColor (255, 0, 0))); +} +json CreateTextEditBox(json jRow, string sPlaceHolderBind, string sValueBind, int nMaxLength, int bMultiline, float fWidth, float fHeight, string sTooltip = "") +{ + json jObject = NuiEnabled(NuiTextEdit(NuiBind(sPlaceHolderBind), NuiBind(sValueBind), nMaxLength, bMultiline), NuiBind(sValueBind + "_event")); + jObject = NuiWidth(jObject, fWidth); + jObject = NuiHeight(jObject, fHeight); + if(sTooltip != "") jObject = NuiTooltip(jObject, NuiBind (sTooltip)); + return JsonArrayInsert(jRow, jObject); +} +json CreateCombo(json jRow, json jList, string sId, float fWidth, float fHeight, string sTooltip = "") +{ + json jCombo; + if(JsonGetType(jList) == JSON_TYPE_NULL) + { + jCombo = NuiId(NuiCombo(NuiBind(sId + "_list"), NuiBind(sId + "_selected")), sId + "_event"); + } + jCombo = NuiId(NuiCombo(jList, NuiBind (sId + "_selected")), sId); + jCombo = NuiEnabled(jCombo, NuiBind (sId + "_event")); + jCombo = NuiWidth(jCombo, fWidth); + jCombo = NuiHeight(jCombo, fHeight); + if(sTooltip != "") jCombo = NuiTooltip(jCombo, NuiBind(sTooltip)); + return JsonArrayInsert(jRow, jCombo); +} +json CreateImage(json jRow, string sResRef, string sId, int nAspect, int nHAlign, int nVAlign, float fWidth, float fHeight, float fMargin = -1.0, string sTooltip = "") +{ + json jImage; + if(sResRef == "") jImage = NuiEnabled(NuiId(NuiImage(NuiBind(sId + "_image"), JsonInt(nAspect), JsonInt(nHAlign), JsonInt(nVAlign)), sId), NuiBind(sId + "_event")); + else jImage = NuiEnabled(NuiId(NuiImage(JsonString(sResRef), JsonInt(nAspect), JsonInt(nHAlign), JsonInt(nVAlign)), sId), NuiBind(sId + "_event")); + jImage = NuiWidth(jImage, fWidth); + jImage = NuiHeight(jImage, fHeight); + if (fMargin > -1.0) jImage = NuiMargin(jImage, fMargin); + if(sTooltip != "") jImage = NuiTooltip(jImage, NuiBind(sTooltip)); + return JsonArrayInsert(jRow, jImage); +} +json CreateCheckBox(json jRow, string sLabel, string sId, float fWidth, float fHeight, string sTooltip = "") +{ + json jCheckBox; + if(sLabel == "") jCheckBox = NuiEnabled(NuiId(NuiCheck(NuiBind(sId + "_label"), NuiBind(sId + "_check")), sId), NuiBind(sId + "_event")); + else jCheckBox = NuiEnabled(NuiId(NuiCheck(JsonString(sLabel), NuiBind(sId + "_check")), sId), NuiBind(sId + "_event")); + jCheckBox = NuiWidth(jCheckBox, fWidth); + jCheckBox = NuiHeight(jCheckBox, fHeight); + if (sTooltip != "") jCheckBox = NuiTooltip (jCheckBox, NuiBind (sTooltip)); + return JsonArrayInsert(jRow, jCheckBox); +} +json CreateSlider(json jRow, string sId, float fWidth, float fHeight, string sTooltip = "") +{ + json jSlider; + jSlider = NuiEnabled(NuiId(NuiSlider(NuiBind(sId + "_value"), NuiBind(sId + "_min"), NuiBind(sId + "_max"), NuiBind(sId + "_stepsize")), sId), NuiBind(sId + "_event")); + jSlider = NuiWidth(jSlider, fWidth); + jSlider = NuiHeight(jSlider, fHeight); + if(sTooltip != "") jSlider = NuiTooltip(jSlider, NuiBind(sTooltip)); + return JsonArrayInsert(jRow, jSlider); +} +json CreateOptions(json jRow, string sId, int nDirection, json jLabels, float fWidth, float fHeight, string sTooltip = "") +{ + json jOption; + jOption = NuiEnabled(NuiId(NuiOptions(nDirection, jLabels, NuiBind(sId + "_value")), sId), NuiBind(sId + "_event")); + jOption = NuiWidth(jOption, fWidth); + jOption = NuiHeight(jOption, fHeight); + if(sTooltip != "") jOption = NuiTooltip (jOption, NuiBind (sTooltip)); + return JsonArrayInsert(jRow, jOption); +} +json CreateList(json jRow, json jElements, string sId, float fRowHeight, float fWidth, float fHeight, string sTooltip = "") +{ + json jList; + jList = NuiId(NuiList(jElements, NuiBind(sId), fRowHeight), sId + "_id"); + jList = NuiWidth(jList, fWidth); + jList = NuiHeight(jList, fHeight); + if (sTooltip != "") jList = NuiTooltip(jList, NuiBind(sTooltip)); + return JsonArrayInsert(jRow, jList); +} +void ai_SetDMWAccessButton(int nButton, int bOn = TRUE) +{ + json jRules = ai_GetCampaignDbJson("rules"); + int nWidgetButtons = JsonGetInt(JsonObjectGet(jRules, sDMWidgetAccessVarname)); + if(bOn) nWidgetButtons = nWidgetButtons | nButton; + else nWidgetButtons = nWidgetButtons & ~nButton; + SetLocalInt(GetModule(), sDMWidgetAccessVarname, nWidgetButtons); + jRules = JsonObjectSet(jRules, sDMWidgetAccessVarname, JsonInt(nWidgetButtons)); + ai_SetCampaignDbJson("rules", jRules); +} +int ai_GetDMWAccessButton(int nButton) +{ + int nWidgetButtons = GetLocalInt(GetModule(), sDMWidgetAccessVarname); + return nWidgetButtons & nButton; +} +void ai_SetDMAIAccessButton(int nButton, int bOn = TRUE) +{ + json jRules = ai_GetCampaignDbJson("rules"); + int nWidgetButtons = JsonGetInt(JsonObjectGet(jRules, sDMAIAccessVarname)); + if(bOn) nWidgetButtons = nWidgetButtons | nButton; + else nWidgetButtons = nWidgetButtons & ~nButton; + SetLocalInt(GetModule(), sDMAIAccessVarname, nWidgetButtons); + jRules = JsonObjectSet(jRules, sDMAIAccessVarname, JsonInt(nWidgetButtons)); + ai_SetCampaignDbJson("rules", jRules); +} +int ai_GetDMAIAccessButton(int nButton) +{ + int nWidgetButtons = GetLocalInt(GetModule(), sDMAIAccessVarname); + return nWidgetButtons & nButton; +} + diff --git a/_module/nss/0i_player_target.nss b/_module/nss/0i_player_target.nss new file mode 100644 index 00000000..bed48348 --- /dev/null +++ b/_module/nss/0i_player_target.nss @@ -0,0 +1,793 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_player_target +//////////////////////////////////////////////////////////////////////////////// + Include script for handling player targeting functions. + +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +#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); +// 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. +void ai_SelectTrap(object oPC, object oAssociate, object oItem); +// Place the selected trap at the location selected by the player for OBJECT_SELF. +void ai_PlaceTrap(object oPC, location lLocation); +// Adds a creature to nGroup for oDM +void ai_AddToGroup(object oDM, object oTarget, string sTargetMode); +// Has nGroup perform an action based on the selected target or location. +void ai_DMAction(object oDM, object oTarget, location lLocation, string sTargetMode); +// Get oPC to select a spell target for oAssociate. +void ai_SelectWidgetSpellTarget(object oPC, object oAssociate, string sElem); +// Updates oAssociates widget by destroying the current one and rebuilding. +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) +{ + object oModule = GetModule(); + string sModuleTargetEvent = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET); + if(sModuleTargetEvent != "") + { + if(sModuleTargetEvent != "0e_player_target") SetLocalString(oModule, AI_MODULE_TARGET_EVENT, sModuleTargetEvent); + } + 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) +{ + object oAssociate = OBJECT_SELF; + if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) && + !ai_GetAIMode(oAssociate, AI_MODE_GHOST) && + !GetLocalInt(oAssociate, sGhostModeVarname)) + { + effect eGhost = EffectCutsceneGhost(); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oAssociate); + SetLocalInt(oAssociate, sGhostModeVarname, TRUE); + } + int nObjectType = GetObjectType(oTarget); + ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, TRUE); + ai_ClearCreatureActions(TRUE); + if(oTarget == GetArea(oPC)) + { + ActionMoveToLocation(lLocation, TRUE); + if(GetLocalObject(oPC, AI_FOLLOW_TARGET) == oAssociate) + { + float fFollowDistance = ai_GetFollowDistance(oPC); + if(GetDistanceBetween(oAssociate, oPC) <= fFollowDistance) + { + DelayCommand(fFollowDistance, AssignCommand(oPC, ActionMoveToObject(oAssociate, TRUE, fFollowDistance))); + } + else AssignCommand(oPC, ActionMoveToObject(oAssociate, TRUE, fFollowDistance)); + } + } + else if(nObjectType == OBJECT_TYPE_CREATURE) + { + if(GetIsDead(oTarget)) + { + AssignCommand(oAssociate, ActionDoCommand(ai_SearchObject(oAssociate, oTarget, oPC, TRUE))); + } + else if(GetIsEnemy(oTarget, oAssociate)) + { + // Lock them into attacking this target only. + SetLocalObject(oAssociate, AI_PC_LOCKED_TARGET, oTarget); + // This resets a henchmens failed Moral save in combat. + if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_coward") + { + 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); + } + ai_SendMessages(GetName(oAssociate) + " is attacking " + GetName(oTarget), AI_COLOR_RED, oPC); + } + else + { + ActionMoveToObject(oTarget, TRUE); + // Player will be stuck with this variable if they are not using the AI. + DeleteLocalInt(oTarget, "AI_I_AM_BEING_HEALED"); + ActionDoCommand(ai_ActionTryHealing(oAssociate, oTarget)); + } + } + else if(nObjectType == OBJECT_TYPE_DOOR) + { + if(GetIsTrapped(oTarget)) + { + if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate); + if(GetTrapDetectedBy(oTarget, oAssociate)) + { + int bStopAction = !GetLocalInt(oTarget, "AI_CANNOT_TRAP_" + GetTag(oAssociate)); + if(ai_ReactToTrap(oAssociate, oTarget, TRUE)) bStopAction = TRUE; + if(bStopAction) + { + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + return; + } + } + } + if(GetLocked(oTarget)) ai_AttemptToByPassLock(oAssociate, oTarget, TRUE); + else 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)) + { + if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate); + if(GetTrapDetectedBy(oTarget, oAssociate)) + { + if(ai_ReactToTrap(oAssociate, oTarget, TRUE)) + { + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + return; + } + + } + } + if(GetLocked(oTarget)) ai_AttemptToByPassLock(oAssociate, oTarget, TRUE); + else ActionDoCommand(ai_SearchObject(oAssociate, oTarget, oPC, TRUE)); + } + else + { + if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS)) + { + AssignCommand(oAssociate, ai_ClearCreatureActions()); + // Check to make sure we are using a melee weapon. + if(ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oAssociate)) || + ai_EquipBestMeleeWeapon(oAssociate)) + { + AssignCommand(oAssociate, ActionWait(1.0)); + AssignCommand(oAssociate, ActionAttack(oTarget)); + } + } + else AssignCommand(oAssociate, DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE)); + } + } + else if(nObjectType == OBJECT_TYPE_TRIGGER) + { + if(GetIsTrapped(oTarget)) + { + if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate); + if(GetTrapDetectedBy(oTarget, oAssociate)) ai_ReactToTrap(oAssociate, oTarget, TRUE); + } + } + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_ActionAllAssociates(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_ActionAssociate(oPC, oTarget, lLocation)); + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation)); + } +} +void ai_SelectFollowTarget(object oPC, object oAssociate, object oTarget) +{ + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + int nToken = NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI); + float fRange = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE) + + StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oAssociate))); + string sRange = FloatToString(fRange, 0, 0); + if(oAssociate == oTarget) + { + ai_SetAIMode(oAssociate, AI_MODE_FOLLOW, FALSE); + DeleteLocalObject(oAssociate, AI_FOLLOW_TARGET); + string sTarget; + if(ai_GetIsCharacter(oAssociate)) + { + sTarget = "nobody"; + ai_SendMessages(GetName(oAssociate) + " is not following anyone now!", AI_COLOR_YELLOW, oPC); + } + else + { + sTarget = GetName(oPC); + ai_SendMessages(GetName(oAssociate) + " is now following " + sTarget + "!", AI_COLOR_YELLOW, oPC); + } + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]"); + } + else + { + ai_SetAIMode(oAssociate, AI_MODE_FOLLOW, TRUE); + SetLocalObject(oAssociate, AI_FOLLOW_TARGET, oTarget); + ai_SendMessages(GetName(oAssociate) + " is now following " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); + AssignCommand(oAssociate, ActionMoveToObject(oTarget, TRUE, ai_GetFollowDistance(oAssociate))); + ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + GetName(oTarget) + " [" + sRange + " meters]"); + } + 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; + int nIndex; + for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++) + { + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oAssociate != OBJECT_INVALID) + { + ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, FALSE); + if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) && + !ai_GetAIMode(oAssociate, AI_MODE_GHOST) && + GetLocalInt(oAssociate, sGhostModeVarname)) + { + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); + } + ExecuteScript("nw_ch_ac1", oAssociate); + } + } + for(nIndex = 2; nIndex < 6; nIndex++) + { + oAssociate = GetAssociate(nIndex, oPC); + if(oAssociate != OBJECT_INVALID) + { + ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, FALSE); + if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) && + !ai_GetAIMode(oAssociate, AI_MODE_GHOST) && + GetLocalInt(oAssociate, sGhostModeVarname)) + { + ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST); + DeleteLocalInt(oAssociate, sGhostModeVarname); + } + ExecuteScript("nw_ch_ac1", oAssociate); + } + } +} +void ai_SelectTrap(object oPC, object oAssociate, object oItem) +{ + if(GetBaseItemType(oItem) != BASE_ITEM_TRAPKIT) + { + ai_SendMessages("A trap kit was not selected.", AI_COLOR_YELLOW, oPC); + return; + } + ai_SendMessages("Now select a location to place the trap.", AI_COLOR_YELLOW, oPC); + SetLocalObject(oAssociate, "AI_TRAP_KIT", oItem); + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_PLACE_TRAP"); + OpenInventory(oAssociate, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_TILE, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_PlaceTrap(object oPC, location lLocation) +{ + object oItem = GetLocalObject(OBJECT_SELF, "AI_TRAP_KIT"); + itemproperty ipTrap = GetFirstItemProperty(oItem); + if(GetItemPropertyType(ipTrap) == ITEM_PROPERTY_TRAP) + { + ActionUseItemAtLocation(oItem, ipTrap, lLocation); + } + else ai_SendMessages("This trap kit does not have a trap property!", AI_COLOR_YELLOW, oPC); +} +void ai_AddToGroup(object oDM, object oTarget, string sTargetMode) +{ + string sGroup = GetStringRight(sTargetMode, 1); + if(oDM == oTarget) + { + ai_SendMessages("Group " + sGroup + " has been cleared.", AI_COLOR_YELLOW, oDM); + string sText = "Group " + sGroup; + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " (Left Action/Right Add)")); + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " (Left Action/Right Add)")); + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_label", JsonString(sText)); + DeleteLocalJson(oDM, "DM_GROUP" + sGroup); + return; + } + string sName = GetName(oTarget); + json jGroup = GetLocalJson(oDM, "DM_GROUP" + sGroup); + if(JsonGetType(jGroup) == JSON_TYPE_NULL) + { + string sText = sName + "'s group"; + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " [Run]")); + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " [Run]")); + NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_label", JsonString(sText)); + jGroup = JsonArrayInsert(JsonArray(), JsonInt(1)); + } + string sUUID = GetObjectUUID(oTarget); + int nIndex = 1; + string sListUUID = JsonGetString(JsonArrayGet(jGroup, nIndex)); + while(sListUUID != "") + { + if(sListUUID == sUUID) + { + ai_SendMessages("This creature is already in the group!", AI_COLOR_RED, oDM); + return; + } + sListUUID = JsonGetString(JsonArrayGet(jGroup, ++nIndex)); + } + jGroup = JsonArrayInsert(jGroup, JsonString(sUUID)); + ai_SendMessages(sName + " has been saved to group" + sGroup, AI_COLOR_YELLOW, oDM); + SetLocalJson(oDM, "DM_GROUP" + sGroup, jGroup); + EnterTargetingMode(oDM, OBJECT_TYPE_CREATURE, MOUSECURSOR_PICKUP, MOUSECURSOR_PICKUP_DOWN); +} +void ai_MonsterAction(object oDM, object oTarget, location lLocation, int bRun, int nIndex) +{ + object oCreature = OBJECT_SELF; + int nObjectType = GetObjectType(oTarget); + ai_ClearCreatureActions(TRUE); + if(oTarget == GetArea(oDM)) + { + ActionMoveToLocation(lLocation, bRun); + } + else if(nObjectType == OBJECT_TYPE_CREATURE) + { + if(GetIsDead(oTarget)) return; + else if(GetIsEnemy(oTarget, oCreature)) + { + // Lock them into attacking this target only. + SetLocalObject(oCreature, AI_PC_LOCKED_TARGET, oTarget); + // This resets a creatures failed Moral save in combat. + if(GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_coward") + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, GetLocalString(oCreature, AI_DEFAULT_SCRIPT)); + } + if(ai_GetIsInCombat(oCreature)) ai_DoMonsterCombatRound(oCreature); + else + { + ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:"); + ai_StartMonsterCombat(oCreature); + } + if(nIndex == 1) + { + ai_SendMessages(GetName(oCreature) + "'s group is attacking " + GetName(oTarget), AI_COLOR_RED, oDM); + } + } + else if(oTarget == oDM) + { + if(GetLocalInt(oCreature, "AI_FOLLOWING_DM")) + { + ClearAllActions(FALSE, oCreature); + DeleteLocalInt(oCreature, "AI_FOLLOWING_DM"); + if(nIndex == 1) + { + ai_SendMessages(GetName(oCreature) + "'s group has stopped following you.", AI_COLOR_RED, oDM); + } + } + else + { + ActionForceFollowObject(oDM, 4.0); + SetLocalInt(oCreature, "AI_FOLLOWING_DM", TRUE); + if(nIndex == 1) + { + ai_SendMessages(GetName(oCreature) + "'s group is following you.", AI_COLOR_RED, oDM); + } + } + } + else + { + ActionMoveToObject(oTarget, TRUE); + // Player will be stuck with this variable if they are not using the AI. + DeleteLocalInt(oTarget, "AI_I_AM_BEING_HEALED"); + ActionDoCommand(ai_ActionTryHealing(oCreature, oTarget)); + if(nIndex == 1) + { + ai_SendMessages(GetName(oCreature) + "'s group is moving to and attempting to heal " + GetName(oTarget), AI_COLOR_RED, oDM); + } + } + } + else if(nObjectType == OBJECT_TYPE_DOOR) + { + if(GetIsTrapped(oTarget)) + { + if(GetTrapDetectedBy(oTarget, oDM)) SetTrapDetectedBy(oTarget, oCreature); + if(GetTrapDetectedBy(oTarget, oCreature)) + { + ai_ReactToTrap(oCreature, oTarget, TRUE); + EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + return; + } + else if(GetLocked(oTarget)) ai_AttemptToByPassLock(oCreature, oTarget); + } + else if(GetLocked(oTarget)) ai_AttemptToByPassLock(oCreature, oTarget); + else 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)) + { + if(GetTrapDetectedBy(oTarget, oDM)) SetTrapDetectedBy(oTarget, oCreature); + if(GetTrapDetectedBy(oTarget, oCreature)) + { + ai_ReactToTrap(oCreature, oTarget, TRUE); + EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + return; + } + if(GetLocked(oTarget)) + { + if(!ai_AttemptToByPassLock(oCreature, oTarget)) + { + AssignCommand(oCreature, ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oTarget) + " is locked!")); + } + EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + return; + } + DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE); + } + else if(GetLocked(oTarget)) + { + if(ai_AttemptToByPassLock(oCreature, oTarget)) + { + AssignCommand(oCreature, ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oTarget) + " is locked!")); + } + EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); + return; + } + ActionDoCommand(ai_SearchObject(oCreature, oTarget, oDM, TRUE)); + } + DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE); + } + else if(nObjectType == OBJECT_TYPE_TRIGGER) + { + if(GetIsTrapped(oTarget)) + { + if(GetTrapDetectedBy(oTarget, oDM)) SetTrapDetectedBy(oTarget, oCreature); + if(GetTrapDetectedBy(oTarget, oCreature)) ai_ReactToTrap(oCreature, oTarget, TRUE); + } + } + EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK); +} +void ai_DMAction(object oDM, object oTarget, location lLocation, string sTargetMode) +{ + string sGroup = GetStringRight(sTargetMode, 1); + json jGroup = GetLocalJson(oDM, "DM_GROUP" + sGroup); + int bRun = JsonGetInt(JsonArrayGet(jGroup, 0)); + int nIndex = 1; + string sUUID = JsonGetString(JsonArrayGet(jGroup, nIndex)); + object oCreature; + while(sUUID != "") + { + oCreature = GetObjectByUUID(sUUID); + AssignCommand(oCreature, ai_MonsterAction(oDM, oTarget, lLocation, bRun, nIndex)); + sUUID = JsonGetString(JsonArrayGet(jGroup, ++nIndex)); + } + if(nIndex == 0) ai_SendMessages("Group" + sGroup + " is empty!", AI_COLOR_RED, oDM); +} +void ai_SelectWidgetSpellTarget(object oPC, object oAssociate, string sElem) +{ + int nIndex; + if(GetStringLength(sElem) == 13) nIndex = StringToInt(GetStringRight(sElem, 2)); + else nIndex = StringToInt(GetStringRight(sElem, 1)); + SetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX", nIndex); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + json jSpell = JsonArrayGet(jWidget, nIndex); + int nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + int nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + if(nClass == -1) // This is an Item. + { + object oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5))); + int nBaseItemType = GetBaseItemType(oItem); + if(Get2DAString("spells", "Range", nSpell) == "P" || // Self + nBaseItemType == BASE_ITEM_ENCHANTED_POTION || + nBaseItemType == BASE_ITEM_POTIONS) + { + 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)); + return; + } + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_USE_ITEM"); + if(nSpell == SPELL_HEALINGKIT) + { + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_MAGIC, MOUSECURSOR_NOMAGIC); + return; + } + } + else // Feats, Spells, Special Abilities. + { + int nFeat = JsonGetInt(JsonArrayGet(jSpell, 5)); + if(nFeat) + { + if(!nSpell || Get2DAString("spells", "Range", nSpell) == "P" || // Self + nFeat == FEAT_SUMMON_FAMILIAR || nFeat == FEAT_ANIMAL_COMPANION || + nFeat == FEAT_TURN_UNDEAD) + { + if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); + // Adjust the spell used for wild shape and other shape feats. + if(nFeat == FEAT_WILD_SHAPE) nSpell += 607; + if(nFeat == FEAT_ELEMENTAL_SHAPE) + { + if(nSpell == 397) nSpell == SUBFEAT_ELEMENTAL_SHAPE_FIRE; + else if(nSpell == 398) nSpell == SUBFEAT_ELEMENTAL_SHAPE_WATER; + else if(nSpell == 399) nSpell == SUBFEAT_ELEMENTAL_SHAPE_EARTH; + else if(nSpell == 400) nSpell == SUBFEAT_ELEMENTAL_SHAPE_AIR; + } + // Do special targeting for attack feats. + if(nFeat == FEAT_STUNNING_FIST || nFeat == FEAT_DIRTY_FIGHTING || + nFeat == FEAT_WHIRLWIND_ATTACK || nFeat == FEAT_QUIVERING_PALM || + nFeat == FEAT_KNOCKDOWN || nFeat == FEAT_IMPROVED_KNOCKDOWN || + nFeat == FEAT_SAP || nFeat == FEAT_KI_DAMAGE || + nFeat == FEAT_DISARM || nFeat == FEAT_IMPROVED_DISARM || + nFeat == FEAT_SMITE_EVIL || nFeat == FEAT_SMITE_GOOD) + { + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_USE_FEAT"); + SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate); + 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)); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + return; + } + SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_USE_FEAT"); + } + 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); + //SendMessageToPC(GetFirstPC(), "nTarget: " + IntToString(nTarget)); + if((nTarget & 1) && !(nTarget & 2) &&!(nTarget & 4)) + { + if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); + ai_CastWidgetSpell(oPC, oAssociate, oAssociate, GetLocation(oAssociate)); + DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate)); + return; + } + if((nTarget & 1) || (nTarget & 2)) nObjectType += OBJECT_TYPE_CREATURE; + if(nTarget & 4) nObjectType += OBJECT_TYPE_TILE; + if(nTarget & 8) nObjectType += OBJECT_TYPE_ITEM; + if(nTarget & 16) nObjectType += OBJECT_TYPE_DOOR; + if(nTarget & 32) nObjectType += OBJECT_TYPE_PLACEABLE; + if(nTarget & 64) nObjectType += OBJECT_TYPE_TRIGGER; + string sShape = Get2DAString("spells", "TargetShape", nSpell); + int nShape, nSetData; + float fRange; + if(oPC == oAssociate) + { + nSetData = TRUE; + fRange = ai_GetSpellRange(nSpell); + if(fRange == 0.1) fRange = 0.0; + } + if(sShape == "sphere") + { + nShape = SPELL_TARGETING_SHAPE_SPHERE; + nSetData = TRUE; + } + else if(sShape == "rectangle") + { + nShape = SPELL_TARGETING_SHAPE_RECT; + nSetData = TRUE; + } + else if(sShape == "hsphere") + { + nShape = SPELL_TARGETING_SHAPE_HSPHERE; + nSetData = TRUE; + } + else if(sShape == "cone") nShape = SPELL_TARGETING_SHAPE_CONE; + else nShape = SPELL_TARGETING_SHAPE_NONE; + if(nSetData) + { + float fSizeX = StringToFloat(Get2DAString("spells", "TargetSizeX", nSpell)); + float fSizeY = StringToFloat(Get2DAString("spells", "TargetSizeY", nSpell)); + int nFlags = StringToInt(Get2DAString("spells", "TargetFlags", nSpell)); + SetEnterTargetingModeData(oPC, nShape, fSizeX, fSizeY, nFlags, fRange); + } + EnterTargetingMode(oPC, nObjectType, MOUSECURSOR_MAGIC, MOUSECURSOR_NOMAGIC); +} +void ai_UpdateAssociateWidget(object oPC, object oAssociate) +{ + int nUIToken = NuiFindWindow(oPC, ai_GetAssociateType(oPC, oAssociate) + AI_WIDGET_NUI); + if(nUIToken) + { + DelayCommand(0.0, NuiDestroy(oPC, nUIToken)); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate)); + /* Not sure why I did this? + if(oPC != oAssociate) + { + nUIToken = NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI); + if(nUIToken) + { + DelayCommand(0.0, NuiDestroy(oPC, nUIToken)); + DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC)); + } + } */ + } +} +int ai_SetActionMode(object oAssociate, int nFeat) +{ + int nMode; + if(nFeat == FEAT_POWER_ATTACK) nMode = ACTION_MODE_POWER_ATTACK; + else if(nFeat == FEAT_RAPID_SHOT) nMode = ACTION_MODE_RAPID_SHOT; + else if(nFeat == FEAT_FLURRY_OF_BLOWS) nMode = ACTION_MODE_FLURRY_OF_BLOWS; + else if(nFeat == FEAT_IMPROVED_POWER_ATTACK) nMode = ACTION_MODE_IMPROVED_POWER_ATTACK; + 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; + if(nMode) + { + SetActionMode(oAssociate, nMode, !GetActionMode(oAssociate, nMode)); + return TRUE; + } + return FALSE; +} diff --git a/_module/nss/0i_spells.nss b/_module/nss/0i_spells.nss new file mode 100644 index 00000000..3c064c91 --- /dev/null +++ b/_module/nss/0i_spells.nss @@ -0,0 +1,2180 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// + Script Name: 0i_spells + Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// + Include scripts for base spells. + +Category: + Enhancement E + Protection P +Indiscriminant I + Discriminant D + Range R + Touch T + Summon S + Healing H + Cure C + +Buff Duration: +1 - All +2 - Short +3 - Long + +Buff Target: + 0 - Caster only + 1-6 Str, Dex, Con, Int, Wis, Cha: Highest Ability Score + 7 - Lowest AC + 8 - Lowest AC without AC Bonus + 9 - Highest Atk + 10 - Most Wounded + 11 - Lowest Fortitude + 12 - Lowest Reflex + 13 - Lowest Will + 14 - Lowest total saves + 15 - Buffs an Item + +Buff Groups: +-1 - Elemental Resistances. +-2 - Summons +-3 - AC (Non armor) +-4 - AC (for Armor/Shield) +-5 - Chance to Miss (Invisibility) +-6 - Regeneration +-7 - Globes of Invulnerablitity +-8 - Damage Reduction +-9 - Mantles +-10 - Alignment vs Chaos +-11 - Alignment vs Evil +-12 - Alignment vs Good +-13 - Alignment vs Law +-14 - Atk Bonus (for Weapon) +-15 - Light effects +-16 - Haste effects +-17 - Polymorph effects +*///////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_messages" +#include "0i_states_cond" +#include "0i_items" +#include "X0_I0_POSITION" +struct stSpell +{ + object oPC; + object oCaster; + object oTarget; + int nBuffType; + int nTarget; + int nPosition; + int nClass; + int nLevel; + 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. +int ai_IsImmuneToPetrification(object oCaster, object oCreature); +// Returns TRUE if oCreature has an effect from a mind affecting spell. +int ai_DoIHaveAMindAffectingSpellOnMe(object oCreature); +// Returns TRUE if nSpell is a cure spell. +int ai_IsCureSpell(int nSpell); +// Returns TRUE if nSpell is an inflict spell. +int ai_IsInflictSpell(int nSpell); +// Returns TRUE if nSpell is an area of effect spell. +int ai_IsAreaOfEffectSpell(int nSpell); +// Returns 1(TRUE) if oAssociate is a spellcaster. +// Rturns 2(TRUE) if oAssociate is a memorizing spellcaster. +int ai_GetIsSpellCaster(object oAssociate); +// Returns TRUE if oCreature is immune to nSpells effects. +int ai_CreatureImmuneToEffect(object oCaster, object oCreature, int nSpell); +// Returns the ranged of nSpell from the spells.2da(Column "Range"). +// S = 8.0f, M = 20.0f, L = 40.0f, T = 5.0f, else = 0.1f; +float ai_GetSpellRange(int nSpell); +// Returns TRUE if oTarget has a spell that we would want to dispel. +// Checks for harmful effects as well as buffing effects. +int ai_CreatureHasDispelableEffect(object oCaster, object oCreature); +// Remove nEffectType of Type specified on oCreature; +// nEffectType uses the constants EFFECT_TYPE_* +void ai_RemoveASpecificEffect(object oCreature, int nEffectType); +// Returns TRUE if oCreature has nEffectType. +// nEffectType uses the constants EFFECT_TYPE_* +int ai_GetHasEffectType(object oCreature, int nEffectType); +// Checks oCreature for special abilities have a long duration. +void ai_CheckCreatureSpecialAbilities(object oCreature); +// Checks oCreature for the silence effect and if the spell only has a somatic component. +int ai_IsSilenced(object oCreature, int nSpell); +// Returns TRUE if ArcaneSpellFailure is too high to chance casting the spell. +int ai_ArcaneSpellFailureTooHigh(object oCreature, int nClass, int nLevel, int nSlot); +// Returns TRUE if oCaster casts nSpell on oTarget. +// This will only cast the spell if oTarget DOES NOT already have the spell +// effect, and the caster has the spell ready. +int ai_TryToCastSpell(object oCaster, int nSpell, object oTarget); +// In "Buff_Target" column the value of 0 in the "ai_spells.2da" references the Caster. +// In "Buff_Target" column this is value 1-6(STR, DEX, CON, INT, WIS, CHA) in the "ai_spells.2da". +object ai_BuffHighestAbilityScoreTarget(object oCaster, int nSpell, int nAbilityScore, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 7 in the "ai_spells.2da". +object ai_BuffLowestACTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 8 in the "ai_spells.2da". +object ai_BuffLowestACWithOutACBonus(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 9 in the "ai_spells.2da". +object ai_BuffHighestAttackTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 10 in the "ai_spells.2da". +object ai_BuffMostWoundedTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 11 in the "ai_spells.2da". +object ai_BuffLowestFortitudeSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 12 in the "ai_spells.2da". +object ai_BuffLowestReflexSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 13 in the "ai_spells.2da". +object ai_BuffLowestWillSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 14 in the "ai_spells.2da". +object ai_BuffLowestSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// In "Buff_Target" column this is value 15 in the "ai_spells.2da". +object ai_BuffItemTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); +// Returns a target for nSpell cast by oCaster based on ai_spells.2da file. +object ai_GetBuffTarget(object oCaster, int nSpell); +// Casts a memorized spell from oCaster of nClass, nSpellLevel, nSpellSlot on oTarget. +void ai_CastMemorizedSpell(object oCaster, int nClass, int nSpellLevel, int nSpellSlot, object oTarget, int bInstant, object oPC = OBJECT_INVALID); +// Casts a known spell from oCaster of nClass, nSpell on oTarget. +void ai_CastKnownSpell(object oCaster, int nClass, int nSpell, object oTarget, int bInstant, object oPC = OBJECT_INVALID); +// Returns true if the spell is cast. +// Checks if they have the spell and will cast it if possible. +int ai_CheckAndCastSpell(object oCaster, int nSpell, int nSpellGroup, float fDelay, object oTarget, object oPC = OBJECT_INVALID); +// Setup monsters for oCaster to buff in ai_CastSpells. +void ai_SetupMonsterBuffTargets(object oCaster); +// Setup the targets for an NPC to buff one of the PC's members or the whole group. +void ai_SetupAllyTargets(object oCaster, object oPC); +// Setup the targets for an NPC to heal one of the PC's members. +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); +// Cycles through a casters spells casting all buffs via actions. +void ai_ActionCastKnownBuff(struct stSpell stSpell); +// 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. +// 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 #. +// nBuffType is the duration 1 - all, 2 - short, 3 - long. +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 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); +// Returns TRUE if oCaster casts a known cure spell on oTarget. +// This uses an action and must use AssignCommand or OBJECT_SELF is the caster! +int ai_CastKnownHealing(object oCreature, object oTarget, object oPC, int nClass); +// Returns TRUE if oCreature has an effect that will break their concentration. +int ai_ConcentrationCondition(object oCreature); +// Check to see if a spell's concentration has been broken, works for summons as well. +void ai_SpellConcentrationCheck(object oCaster); +// Returns TRUE if oCreature can safely cast nSpell defensively or has a good +// chance of casting while in melee. +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 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. +void ai_CastWidgetSpell(object oPC, object oAssociate, object oTarget, location lLocation); +// Uses the feat on the current target for oAssociate. +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; + while(nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + nClassPosition = GetClassByPosition(nPosition, oCreature); + if(nClassPosition == CLASS_TYPE_INVALID) return FALSE; + if(nClass = nClassPosition) + { + if(Get2DAString("classes", "SpellCaster", nClass) == "1") + { + nSlot = 0; + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + nMaxSlots = GetMemorizedSpellCountByLevel(oCreature, nClass, nLevel); + while(nSlot < nMaxSlots) + { + if(GetMemorizedSpellId(oCreature, nClass, nLevel, nSlot) == nSpell && + GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot)) return TRUE; + nSlot++; + } + } + else return GetSpellUsesLeft(oCreature, nClass, nSpell, nMetaMagic, nDomain); + } + } + nPosition++; + } + return FALSE; +} +int ai_IsImmuneToPetrification(object oCaster, object oCreature) +{ + int nAppearance = GetAppearanceType(oCreature); + switch(nAppearance) + { + case APPEARANCE_TYPE_BASILISK: + case APPEARANCE_TYPE_COCKATRICE: + case APPEARANCE_TYPE_MEDUSA: + case APPEARANCE_TYPE_ALLIP: + case APPEARANCE_TYPE_ELEMENTAL_AIR: + case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_EARTH: + case APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_FIRE: + case APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER: + case APPEARANCE_TYPE_ELEMENTAL_WATER: + case APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER: + case APPEARANCE_TYPE_GOLEM_STONE: + case APPEARANCE_TYPE_GOLEM_IRON: + case APPEARANCE_TYPE_GOLEM_CLAY: + case APPEARANCE_TYPE_GOLEM_BONE: + case APPEARANCE_TYPE_GORGON: + case APPEARANCE_TYPE_HEURODIS_LICH: + case APPEARANCE_TYPE_LANTERN_ARCHON: + case APPEARANCE_TYPE_SHADOW: + case APPEARANCE_TYPE_SHADOW_FIEND: + case APPEARANCE_TYPE_SHIELD_GUARDIAN: + case APPEARANCE_TYPE_SKELETAL_DEVOURER: + case APPEARANCE_TYPE_SKELETON_CHIEFTAIN: + case APPEARANCE_TYPE_SKELETON_COMMON: + case APPEARANCE_TYPE_SKELETON_MAGE: + case APPEARANCE_TYPE_SKELETON_PRIEST: + case APPEARANCE_TYPE_SKELETON_WARRIOR: + case APPEARANCE_TYPE_SKELETON_WARRIOR_1: + case APPEARANCE_TYPE_SPECTRE: + case APPEARANCE_TYPE_WILL_O_WISP: + case APPEARANCE_TYPE_WRAITH: + case APPEARANCE_TYPE_BAT_HORROR: + case 405: // Dracolich: + case 415: // Alhoon + case 418: // shadow dragon + case 420: // mithral golem + case 421: // admantium golem + case 430: // Demi Lich + case 469: // animated chest + case 474: // golems + case 475: // golems + return TRUE; + } + // Petrification immunity can also be granted as an item property. + if(ResistSpell(oCaster, oCreature) == 2 ) return TRUE; + // Prevent people from petrifying DM, resulting in GUI even when effect is not successful. + if(!GetPlotFlag(oCreature) && GetIsDM(oCreature)) return TRUE; + return FALSE; +} +int ai_DoIHaveAMindAffectingSpellOnMe(object oCreature) +{ + if(GetHasSpellEffect(SPELL_SLEEP, oCreature) || + GetHasSpellEffect(SPELL_DAZE, oCreature) || + GetHasSpellEffect(SPELL_HOLD_ANIMAL, oCreature) || + GetHasSpellEffect(SPELL_HOLD_MONSTER, oCreature) || + GetHasSpellEffect(SPELL_HOLD_PERSON, oCreature) || + GetHasSpellEffect(SPELL_CHARM_MONSTER, oCreature) || + GetHasSpellEffect(SPELL_CHARM_PERSON, oCreature) || + GetHasSpellEffect(SPELL_CHARM_PERSON_OR_ANIMAL, oCreature) || + GetHasSpellEffect(SPELL_MASS_CHARM, oCreature) || + GetHasSpellEffect(SPELL_DOMINATE_ANIMAL, oCreature) || + GetHasSpellEffect(SPELL_DOMINATE_MONSTER, oCreature) || + GetHasSpellEffect(SPELL_DOMINATE_PERSON, oCreature) || + GetHasSpellEffect(SPELL_CONFUSION, oCreature) || + GetHasSpellEffect(SPELL_MIND_FOG, oCreature) || + GetHasSpellEffect(SPELL_CLOUD_OF_BEWILDERMENT, oCreature) || + GetHasSpellEffect(SPELLABILITY_BOLT_DOMINATE,oCreature) || + GetHasSpellEffect(SPELLABILITY_BOLT_CHARM,oCreature) || + GetHasSpellEffect(SPELLABILITY_BOLT_CONFUSE,oCreature) || + GetHasSpellEffect(SPELLABILITY_BOLT_DAZE,oCreature)) return TRUE; + return FALSE; +} +int ai_IsCureSpell(int nSpell) +{ + switch(nSpell) + { + case SPELL_CURE_CRITICAL_WOUNDS: + case SPELL_CURE_LIGHT_WOUNDS: + case SPELL_CURE_MINOR_WOUNDS: + case SPELL_CURE_MODERATE_WOUNDS: + case SPELL_CURE_SERIOUS_WOUNDS: + case SPELL_HEAL: return TRUE; break; + } + return FALSE; +} +int ai_IsInflictSpell(int nSpell) +{ + switch(nSpell) + { + case SPELL_INFLICT_CRITICAL_WOUNDS: + case SPELL_INFLICT_LIGHT_WOUNDS: + case SPELL_INFLICT_MINOR_WOUNDS: + case SPELL_INFLICT_MODERATE_WOUNDS: + case SPELL_INFLICT_SERIOUS_WOUNDS: + case SPELL_HARM: return TRUE; break; + } + return FALSE; +} +int ai_IsAreaOfEffectSpell(int nSpell) +{ + switch(nSpell) + { + case SPELL_ACID_FOG : + case SPELL_MIND_FOG : + case SPELL_STORM_OF_VENGEANCE: + case SPELL_WEB : + case SPELL_GREASE : + case SPELL_CREEPING_DOOM : +// case SPELL_DARKNESS : + case SPELL_SILENCE : + case SPELL_BLADE_BARRIER : + case SPELL_CLOUDKILL : + case SPELL_STINKING_CLOUD : + case SPELL_WALL_OF_FIRE : + case SPELL_INCENDIARY_CLOUD : + case SPELL_ENTANGLE : + case SPELL_EVARDS_BLACK_TENTACLES: + case SPELL_CLOUD_OF_BEWILDERMENT : + case SPELL_STONEHOLD : + case SPELL_VINE_MINE : + case SPELL_SPIKE_GROWTH : + case SPELL_DIRGE : + case 530 : // vine mine + case 531 : // vine mine + case 532 : // vine mine + case 961 : // Prismatic Sphere + return TRUE; + } + return FALSE; +} +int ai_GetIsSpellCaster(object oAssociate) +{ + int nIndex, nSpellCaster, nClass; + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClass = GetClassByPosition(nIndex, oAssociate); + if(nClass == CLASS_TYPE_INVALID) return nSpellCaster; + if(Get2DAString("classes", "SpellCaster", nClass) == "1") + { + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") return 2; + else nSpellCaster = 1; + } + } + return nSpellCaster; +} +int ai_GetIsSpellBookRestrictedCaster(object oAssociate) +{ + int nIndex, nSpellCaster, nClass; + for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) + { + nClass = GetClassByPosition(nIndex, oAssociate); + if(nClass == CLASS_TYPE_INVALID) return FALSE; + if(Get2DAString("classes", "SpellbookRestricted", nClass) == "1") return TRUE; + } + return FALSE; +} +int ai_CreatureImmuneToEffect(object oCaster, object oCreature, int nSpell) +{ + string sIType = Get2DAString("ai_spells", "ImmunityType", nSpell); + 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 == "Fear" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_FEAR) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + else if(sIType == "Sleep" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_SLEEP) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + else if(sIType == "Paralysis" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_PARALYSIS) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + else if(sIType == "Domination" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_DOMINATE) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + else if(sIType == "Confusion" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_CONFUSED) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + else if(sIType == "Blindness" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_BLINDNESS) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + else if(sIType == "Dazed" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_DAZED) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; + else if(sIType == "Charm" && + (GetIsImmune(oCreature, IMMUNITY_TYPE_CHARM) || + GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) 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!"); + 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!"); + 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)); + 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", "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; + } + } + } + int nLevel = StringToInt(Get2DAString("spells", "Innate", nSpell)); + // Globe spells should be checked... + if((GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, oCreature) || + GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, oCreature)) && + nLevel < 4 && d100() < 75) return TRUE; + if(GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, oCreature) && + nLevel < 5 && d100() < 75) return TRUE; + // Check creatures items for immunity. + int nIndex; + json jSpellImmunity = GetLocalJson(oCreature, AI_TALENT_IMMUNITY); + json jSpell = JsonArrayGet(jSpellImmunity, nIndex); + while(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + if(nSpell == JsonGetInt(jSpell)) + { + if(AI_DEBUG) ai_Debug("0i_spells", "407", 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."); + return FALSE; +} +float ai_GetSpellRange(int nSpell) +{ + string sRange = Get2DAString("spells", "Range", nSpell); + if(sRange == "S") return AI_SHORT_DISTANCE; + else if(sRange == "M") return AI_MEDIUM_DISTANCE; + else if(sRange == "L") return AI_LONG_DISTANCE; + else if(sRange == "T") return AI_RANGE_MELEE; + return 0.1; +} +int ai_CreatureHasDispelableEffect(object oCaster, object oCreature) +{ + int nSpellID, nLastSpellID, bSpell, nDispelChance; + // Cycle through the targets effects. + effect eEffect = GetFirstEffect(oCreature); + if(AI_DEBUG) ai_Debug("0i_spells", "485", "nSpell: " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", GetEffectSpellId(eEffect)))) + + " oCreature: " + GetName(oCreature)); + while(GetIsEffectValid(eEffect)) + { + nSpellID = GetEffectSpellId(eEffect); + // -1 is not a spell. + if(AI_DEBUG) ai_Debug("0i_spells", "491", "nSpell: (" + IntToString(nSpellID) + ") " + + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpellID)))); + if(nSpellID > -1 && nLastSpellID != nSpellID) + { + // We check if the spell is Hostile(-1) or Helpful(+1). + if(Get2DAString("ai_spells", "HostileSetting", nSpellID) == "1") nDispelChance--; + else nDispelChance++; + if(AI_DEBUG) ai_Debug("0i_spells", "497", "HostileSetting: " + Get2DAString("ai_spells", "HostileSetting", nSpellID) + + " nDispelChance: " + IntToString(nDispelChance)); + } + nLastSpellID = nSpellID; + eEffect = GetNextEffect(oCreature); + } + // if the target has more Helpful spells than harmful spells effecting them + // then use dispel! + if(AI_DEBUG) ai_Debug("0i_spells", "505", "nDispelChance: " + IntToString(nDispelChance)); + return (nDispelChance > 0); +} +void ai_RemoveASpecificEffect(object oCreature, int nEffectType) +{ + effect eEffect = GetFirstEffect(oCreature); + //Search for the effect. + while(GetIsEffectValid(eEffect)) + { + if(GetEffectType(eEffect) == nEffectType) + { + //Remove effect. + RemoveEffect(oCreature, eEffect); + eEffect = GetFirstEffect(oCreature); + } + else eEffect = GetNextEffect(oCreature); + } +} +int ai_GetHasEffectType(object oCreature, int nEffectType) +{ + effect eEffect = GetFirstEffect(oCreature); + while(GetIsEffectValid(eEffect)) + { + if(GetEffectType(eEffect, TRUE) == nEffectType) return TRUE; + eEffect = GetNextEffect(oCreature); + } + return FALSE; +} +void ai_CheckCreatureSpecialAbilities(object oCreature) +{ + int nMaxSpecialAbilities = GetSpellAbilityCount(oCreature); + if(nMaxSpecialAbilities) + { + int nIndex, bCanCast; + // Struct is id, ready, level. + int nSpell; + while(nIndex < nMaxSpecialAbilities) + { + nSpell = GetSpellAbilitySpell(oCreature, nIndex); + if(GetSpellAbilityReady(oCreature, nSpell)) + { + bCanCast = FALSE; + if(GetSpellAbilityCasterLevel(oCreature, nIndex) > 4) + { + // 1 Min/Lvl spell that is too low of level so it must be cast at 5th lvl or greater. + if(nSpell == SPELL_FLAME_WEAPON) bCanCast = TRUE; + else if(nSpell == SPELL_BLESS) bCanCast = TRUE; + else if(nSpell == SPELL_AID) bCanCast = TRUE; + else if(nSpell == SPELL_DEATH_WARD) bCanCast = TRUE; + } + if(nSpell == SPELL_ENERGY_BUFFER) bCanCast = TRUE; + else if(nSpell == SPELL_PROTECTION_FROM_ELEMENTS) bCanCast = TRUE; + else if(nSpell == SPELL_RESIST_ELEMENTS) bCanCast = TRUE; + else if(nSpell == SPELL_ENDURE_ELEMENTS) bCanCast = TRUE; + else if(nSpell == SPELL_MAGE_ARMOR) bCanCast = TRUE; + else if(nSpell == SPELL_MAGIC_VESTMENT) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_MAGIC_WEAPON) bCanCast = TRUE; + else if(nSpell == SPELL_MAGIC_WEAPON) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_IX) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_VIII) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_VII) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_VI) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_V) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_IV) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_III) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_II) bCanCast = TRUE; + else if(nSpell == SPELL_SUMMON_CREATURE_I) bCanCast = TRUE; + else if(nSpell == SPELL_BARKSKIN) bCanCast = TRUE; + else if(nSpell == SPELL_SHIELD) bCanCast = TRUE; + else if(nSpell == SPELL_ENTROPIC_SHIELD) bCanCast = TRUE; + else if(nSpell == SPELL_SHIELD_OF_FAITH) bCanCast = TRUE; + else if(nSpell == SPELL_REMOVE_FEAR) bCanCast = TRUE; + else if(nSpell == SPELL_IRONGUTS) bCanCast = TRUE; + else if(nSpell == SPELL_PREMONITION) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_STONESKIN) bCanCast = TRUE; + else if(nSpell == SPELL_GHOSTLY_VISAGE) bCanCast = TRUE; + else if(nSpell == SPELL_IMPROVED_INVISIBILITY) bCanCast = TRUE; + else if(nSpell == SPELL_INVISIBILITY_SPHERE) bCanCast = TRUE; + else if(nSpell == SPELL_INVISIBILITY) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_BULLS_STRENGTH) bCanCast = TRUE; + else if(nSpell == SPELL_BULLS_STRENGTH) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_CATS_GRACE) bCanCast = TRUE; + else if(nSpell == SPELL_CATS_GRACE) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_EAGLE_SPLENDOR) bCanCast = TRUE; + else if(nSpell == SPELL_EAGLE_SPLEDOR) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_ENDURANCE) bCanCast = TRUE; + else if(nSpell == SPELL_ENDURANCE) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_FOXS_CUNNING) bCanCast = TRUE; + else if(nSpell == SPELL_FOXS_CUNNING) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_OWLS_WISDOM) bCanCast = TRUE; + else if(nSpell == SPELL_OWLS_WISDOM) bCanCast = TRUE; + else if(nSpell == SPELL_KEEN_EDGE) bCanCast = TRUE; + else if(nSpell == SPELL_ANIMATE_DEAD) bCanCast = TRUE; + else if(nSpell == SPELL_INVISIBILITY_PURGE) bCanCast = TRUE; + else if(nSpell == SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE) bCanCast = TRUE; + else if(nSpell == SPELL_DARKFIRE) bCanCast = TRUE; + else if(nSpell == SPELL_NEGATIVE_ENERGY_PROTECTION) bCanCast = TRUE; + else if(nSpell == SPELL_MAGIC_CIRCLE_AGAINST_GOOD) bCanCast = TRUE; + else if(nSpell == SPELL_FREEDOM_OF_MOVEMENT) bCanCast = TRUE; + else if(nSpell == SPELL_NEUTRALIZE_POISON) bCanCast = TRUE; + else if(nSpell == SPELL_MIND_BLANK) bCanCast = TRUE; + else if(nSpell == SPELL_LESSER_MIND_BLANK) bCanCast = TRUE; + else if(nSpell == SPELL_SPELL_RESISTANCE) bCanCast = TRUE; + else if(nSpell == SPELL_PROTECTION_FROM_GOOD) bCanCast = TRUE; + else if(nSpell == SPELL_CREATE_UNDEAD) bCanCast = TRUE; + else if(nSpell == SPELL_PLANAR_ALLY) bCanCast = TRUE; + else if(nSpell == SPELL_LESSER_PLANAR_BINDING) bCanCast = TRUE; + else if(nSpell == SPELL_ETHEREALNESS) bCanCast = TRUE; + else if(nSpell == SPELL_PROTECTION_FROM_SPELLS) bCanCast = TRUE; + else if(nSpell == SPELL_SHADOW_SHIELD) bCanCast = TRUE; + else if(nSpell == SPELL_CREATE_GREATER_UNDEAD) bCanCast = TRUE; + else if(nSpell == SPELL_GREATER_PLANAR_BINDING) bCanCast = TRUE; + if(bCanCast && GetSpellAbilityReady(oCreature, nIndex)) + { + ActionCastSpellAtObject(nSpell, oCreature, 255, 0, 0, 0, TRUE); + } + } + nIndex++; + } + } +} +int ai_IsSilenced(object oCreature, int nSpell) +{ + if(Get2DAString("spells", "VS", nSpell) == "s") return FALSE; + if(ai_GetHasEffectType(oCreature, EFFECT_TYPE_SILENCE)) return TRUE; + return FALSE; +} +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) + + " Arcane Spell Failure: " + IntToString(GetArcaneSpellFailure(oCreature)) + + " AI_ASF_WILL_USE: " + IntToString(AI_ASF_WILL_USE)); + if(Get2DAString("classes", "ASF", nClass) == "1" && + GetArcaneSpellFailure(oCreature) > AI_ASF_WILL_USE) + { + if(GetMemorizedSpellMetaMagic(oCreature, nClass, nLevel, nSlot) == METAMAGIC_STILL) return FALSE; + return TRUE; + } + return FALSE; +} +int ai_TryToCastSpell(object oCaster, int nSpell, object oTarget) +{ + if(GetHasSpell(nSpell, oCaster) && !GetHasSpellEffect(nSpell, oTarget)) + { + ActionCastSpellAtObject(nSpell, oTarget); + return TRUE; + } + return FALSE; +} +int ai_SpellGroupNotCast(object oCreature, string sBuffGroup) +{ + return !GetLocalInt(oCreature, sBuffGroup); +} +void ai_ClearSpellsCastGroups(object oCreature) +{ + int nCounter; + for(nCounter = -1; nCounter <= AI_BUFF_GROUPS; nCounter--) + { + DeleteLocalInt(oCreature, "AI_USED_SPELL_GROUP_" + IntToString(nCounter)); + } +} +int ai_CanUseSpell(object oCaster, object oTarget, int nSpell, int nTargetType) +{ + // Should we ignore associates? + if(ai_GetAIMode(oCaster, AI_MODE_IGNORE_ASSOCIATES) && + GetAssociateType(oTarget) > 1) return FALSE; + // For ability scores we return a bonus to the ability to be checked against + // the target with the highest ability getting the spell first. + if(nTargetType == 1) // Ability score buff for strength. + { + // We don't want to buff the strength for someone using weapon finesse! + if(GetHasFeat(FEAT_WEAPON_FINESSE, oTarget)) return -5; + return TRUE; + } + if(nTargetType == 7) // Lowest AC. + { + // Stone bones only effects the undead. + if(nSpell == SPELL_STONE_BONES) + { + if(GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) return FALSE; + } + return TRUE; + } + if(nTargetType == 8) // Lowest AC without AC Bonus. + { + if(nSpell == SPELL_MAGIC_VESTMENT) + { + object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + if(oArmor == OBJECT_INVALID) return FALSE; + } + return TRUE; + } + if(nTargetType == 9) // Highest Attack. + { + return TRUE; + } + if(nTargetType == 10) // Most wounded, Lowest Hp. + { + return TRUE; + } + if(nTargetType == 11) // Lowest Fortitude save. + { + return TRUE; + } + if(nTargetType == 12) // Lowest Reflex save. + { + return TRUE; + } + if(nTargetType == 13) // Lowest Will save. + { + return TRUE; + } + if(nTargetType == 14) // Lowest Save. + { + return TRUE; + } + if(nSpell == SPELL_MAGIC_FANG) + { + object oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster); + if(oTarget != oCompanion) return FALSE; + } + return TRUE; +} +// Used to check if the targets weapon can be buffed by the spells effects. +int ai_CanItemBeBuffed(int nSpell, object oTarget) +{ + object oWeapon, oArmor; + if(nSpell == SPELL_MAGIC_WEAPON || nSpell == SPELL_GREATER_MAGIC_WEAPON || + nSpell == SPELL_BLADE_THIRST) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ENHANCEMENT_BONUS)) return FALSE; + } + else if(nSpell == SPELL_MAGIC_VESTMENT) + { + oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + if(oArmor == OBJECT_INVALID) return FALSE; + if(ai_GetHasItemProperty(oArmor, ITEM_PROPERTY_AC_BONUS)) return FALSE; + } + else if(nSpell == SPELL_DARKFIRE) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, 127)) return FALSE; + } + else if(nSpell == SPELL_FLAME_WEAPON) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, 124)) return FALSE; + } + else if(nSpell == SPELL_KEEN_EDGE) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsSlashingWeapon(oWeapon)) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_KEEN)) return FALSE; + } + else if(nSpell == SPELL_DEAFENING_CLANG) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, 137)) return FALSE; + } + else if(nSpell == SPELL_BLESS_WEAPON) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP, IP_CONST_RACIALTYPE_UNDEAD)) return FALSE; + } + else if(nSpell == SPELL_HOLY_SWORD) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_HOLY_AVENGER)) return FALSE; + } + else if(nSpell == SPELL_BLACKSTAFF) + { + oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(GetBaseItemType(oWeapon) != BASE_ITEM_QUARTERSTAFF) return FALSE; + if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, IP_CONST_ONHIT_DISPELMAGIC)) return FALSE; + } + return TRUE; +} +// In "Buff_Target" column the value of 0 in the "ai_spells.2da" references the Caster. +// In "Buff_Target" column this is value 1-6(STR, DEX, CON, INT, WIS, CHA) in the "ai_spells.2da". +object ai_BuffHighestAbilityScoreTarget(object oCaster, int nSpell, int nAbilityScore, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup)) return oMaster; + } + int nCntr = 1, nAB, nHighAB, nTarget, nUseSpell; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange) + { + nUseSpell = ai_CanUseSpell(oCaster, oTarget, nSpell, nAbilityScore + 1); + if(nUseSpell == 0) {} + else + { + nAB = GetAbilityScore(oTarget, nAbilityScore) + nUseSpell; + if(nAB > nHighAB) + {nHighAB = nAB; nTarget = nCntr; } + } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); +} +// In "Buff_Target" column this is value 7 in the "ai_spells.2da". +object ai_BuffLowestACTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + object oMaster = GetMaster(); + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 7)) return oMaster; + } + int nCntr = 1, nAC, nLowAC = 100, nTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nAC = GetAC(oTarget); + if(nAC < nLowAC && ai_CanUseSpell(oCaster, oTarget, nSpell, 7)) + {nLowAC = nAC; nTarget = nCntr; } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); + return oTarget; +} +// In "Buff_Target" column this is value 8 in the "ai_spells.2da". +object ai_BuffLowestACWithOutACBonus(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 8)) return oMaster; + } + int nCntr = 1, nAC, nLowAC = 50, nTarget; + object oItem, oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nAC = GetAC(oTarget); + oItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); + if(nAC < nLowAC && ai_CanUseSpell(oCaster, oTarget, nSpell, 8) && + !GetItemHasItemProperty(oItem, ITEM_PROPERTY_AC_BONUS)) + { + nLowAC = nAC; + nTarget = nCntr; + } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); +} +// In "Buff_Target" column this is value 9 in the "ai_spells.2da". +object ai_BuffHighestAttackTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 9)) return oMaster; + } + int nCntr = 1, nAtk, nHighAtk, nTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nAtk = GetBaseAttackBonus(oTarget); + if(nAtk > nHighAtk && ai_CanUseSpell(oCaster, oTarget, nSpell, 9)) + {nHighAtk = nAtk; nTarget = nCntr; } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); + return oTarget; +} +// In "Buff_Target" column this is value 10 in the "ai_spells.2da". +object ai_BuffMostWoundedTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 9)) return oMaster; + } + int nCntr = 1, nDmg, nMostDmg, nHp, nLowHp = 10000, nTarget, nHpTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && + ai_SpellGroupNotCast(oTarget, sBuffGroup) && + ai_CanUseSpell(oCaster, oTarget, nSpell, 10)) + { + nHp = GetCurrentHitPoints(oTarget); + nDmg = GetMaxHitPoints(oTarget) - nHp; + if(nDmg > nMostDmg) { nMostDmg = nDmg; nTarget = nCntr; } + if(nHp < nLowHp) { nLowHp = nHp; nHpTarget = nCntr; } + } + // If no one is damage then put regeneration on the lowest hp target. + if(nMostDmg == 0) nTarget = nHpTarget; + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); +} +// In "Buff_Target" column this is value 11 in the "ai_spells.2da". +object ai_BuffLowestFortitudeSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 11)) return oMaster; + } + int nCntr = 1, nSave, nLowSave = 100, nTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nSave = GetFortitudeSavingThrow(oTarget); + if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 11)) + {nLowSave = nSave; nTarget = nCntr; } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); +} +// In "Buff_Target" column this is value 12 in the "ai_spells.2da". +object ai_BuffLowestReflexSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 12)) return oMaster; + } + int nCntr = 1, nSave, nLowSave = 100, nTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nSave = GetReflexSavingThrow(oTarget); + if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 12)) + {nLowSave = nSave; nTarget = nCntr; } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); +} +// In "Buff_Target" column this is value 13 in the "ai_spells.2da". +object ai_BuffLowestWillSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 13)) return oMaster; + } + int nCntr = 1, nSave, nLowSave = 100, nTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nSave = GetWillSavingThrow(oTarget); + if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 13)) + {nLowSave = nSave; nTarget = nCntr; } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); +} +// In "Buff_Target" column this is value 14 in the "ai_spells.2da". +object ai_BuffLowestSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(!GetHasSpellEffect(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup) && + ai_CanUseSpell(oCaster, oMaster, nSpell, 14)) return oMaster; + } + int nCntr = 1, nSave, nLowSave = 200, nTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nSave = GetFortitudeSavingThrow(oTarget) + GetReflexSavingThrow(oTarget) + GetWillSavingThrow(oTarget); + if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 14)) + {nLowSave = nSave; nTarget = nCntr; } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); +} +// In "Buff_Target" column this is value 15 in the "ai_spells.2da". +object ai_BuffItemTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") +{ + if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) + { + object oMaster = GetMaster(); + if(ai_CanItemBeBuffed(nSpell, oMaster) && + ai_SpellGroupNotCast(oMaster, sBuffGroup)) return oMaster; + } + int nCntr = 1, nAtk, nHighAtk = -9999, nTarget; + object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); + while (nCntr < 10) + { + if(oTarget != OBJECT_INVALID && ai_CanItemBeBuffed(nSpell, oTarget) && + GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) + { + nAtk = GetBaseAttackBonus(oTarget); + if(nAtk > nHighAtk) + { nHighAtk = nAtk; nTarget = nCntr; } + } + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); + } + if(nTarget == 0) return OBJECT_INVALID; + oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); + return oTarget; +} +object ai_GetBuffTarget(object oCaster, int nSpell) +{ + object oTarget = OBJECT_INVALID; + string sGroup = Get2DAString("ai_spells", "Buff_Group", nSpell); + if(sGroup == "") sGroup = IntToString(nSpell); + string sBuffGroup = "AI_USED_SPELL_GROUP_" + sGroup; + string sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell); + if(AI_DEBUG) ai_Debug("0i_spells", "769", "BuffTarget: " + sBuffTarget); + if(sBuffTarget == "0") + { + if(ai_SpellGroupNotCast(oCaster, sBuffGroup) && + !GetHasSpellEffect(nSpell, oCaster) && + ai_CanUseSpell(oCaster, oTarget, nSpell, 0)) + { + oTarget = oCaster; + } + } + else if(sBuffTarget == "1") + oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_STRENGTH, "", AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "2") + oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_DEXTERITY, "", AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "3") + oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_CONSTITUTION, "", AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "4") + oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_INTELLIGENCE, "", AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "5") + oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_WISDOM, "", AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "6") + oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_CHARISMA, "", AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "7") + oTarget = ai_BuffLowestACTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "8") + oTarget = ai_BuffLowestACWithOutACBonus(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "9") + oTarget = ai_BuffHighestAttackTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "10") + oTarget = ai_BuffMostWoundedTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "11") + oTarget = ai_BuffLowestFortitudeSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "12") + oTarget = ai_BuffLowestReflexSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "13") + oTarget = ai_BuffLowestWillSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "14") + oTarget = ai_BuffLowestSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + else if(sBuffTarget == "15") + oTarget = ai_BuffItemTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); + if(oTarget != OBJECT_INVALID) + { + SetLocalInt(oTarget, sBuffGroup, TRUE); + DelayCommand(6.0, DeleteLocalInt(oTarget, sBuffGroup)); + } + if(AI_DEBUG) ai_Debug("0i_spells", "939", GetName(oCaster) + " is targeting " + GetName(oTarget) + + " with " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))) + " spell" + + " sBuffGroup: " + sBuffGroup + "."); + return oTarget; +} +void ai_CastMemorizedSpell(object oCaster, int nClass, int nSpellLevel, int nSpellSlot, object oTarget, int bInstant, object oPC = OBJECT_INVALID) +{ + int nDomain; + int nSpell = GetMemorizedSpellId(oCaster, nClass, nSpellLevel, nSpellSlot); + if(GetMemorizedSpellIsDomainSpell(oCaster, nClass, nSpellLevel, nSpellSlot) == 1) nDomain = nSpellLevel; + else nDomain = 0; + int nMetaMagic = GetMemorizedSpellMetaMagic(oCaster, nClass, nSpellLevel, nSpellSlot); + if(AI_DEBUG) ai_Debug("0i_spells", "951", "nSpell: " + IntToString(nSpell) + " oTarget: " + GetName(oTarget) + + " nMetaMagic: " + IntToString(nMetaMagic) + " nDomain: " + IntToString(nDomain) + + " bInstant: " + IntToString(bInstant) + " nClass: " + IntToString(nClass)); + ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain, 0, bInstant); + // Right now I cannot get nClass to work here... + //DelayCommand(fDelay, ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain, 0, TRUE, nClass)); + if(oPC != OBJECT_INVALID) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + ai_SendMessages(GetName(oCaster) + " has cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_GREEN, oPC); + } +} +void ai_CastKnownSpell(object oCaster, int nClass, int nSpell, object oTarget, int bInstant, object oPC = OBJECT_INVALID) +{ + if(AI_DEBUG) ai_Debug("0i_Spells", "965", GetName(oCaster) + " is casting " + IntToString(nSpell)); + ActionCastSpellAtObject(nSpell, oTarget, 255, FALSE, 0, 0, bInstant); + // Right now I cannot get nClass to work here... + //ActionCastSpellAtObject(nSpell, oTarget, 255, FALSE, 0, 0, TRUE, nClass); + if(oPC != OBJECT_INVALID) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + ai_SendMessages(GetName(oCaster) + " has cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_GREEN, oPC); + } +} +int ai_CheckAndCastSpell(object oCaster, int nSpell, int nSpellGroup, float fDelay, object oTarget, object oPC = OBJECT_INVALID) +{ + int nClassCnt = 1, nClass, nMaxSlot, nSpellLevel, nSpellSlot, nMemorizedSpell, nDomain, nMetaMagic; + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + while(nClassCnt <= AI_MAX_CLASSES_PER_CHARACTER && nClass != CLASS_TYPE_INVALID) + { + nClass = GetClassByPosition(nClassCnt); + // Search all memorized spells for the spell. + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + // Check each level starting with the highest to lowest. + nSpellLevel = 0; + while(nSpellLevel < 10) + { + // Check each slot within each level. + nMaxSlot = GetMemorizedSpellCountByLevel(oCaster, nClass, nSpellLevel); + nSpellSlot = 0; + while(nSpellSlot < nMaxSlot) + { + if(GetMemorizedSpellReady(oCaster, nClass, nSpellLevel, nSpellSlot)) + { + nMemorizedSpell = GetMemorizedSpellId(oCaster, nClass, nSpellLevel, nSpellSlot); + if(nMemorizedSpell == nSpell) + { + ai_CastMemorizedSpell(oCaster, nClass, nSpellLevel, nSpellSlot, oTarget, FALSE, oPC); + return TRUE; + } + } + nSpellSlot++; + } + nSpellLevel++; + } + } + // Check non-memorized known lists for the spell. + else if(GetSpellUsesLeft(oCaster, nClass, nSpell)) + { + ai_CastKnownSpell(oCaster, nClass, nSpell, oTarget, FALSE, oPC); + return TRUE; + } + nClassCnt++; + } + return FALSE; +} +void ai_SetupMonsterBuffTargets(object oCaster) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1020", GetName(oCaster) + " is setting buff targets."); + SetLocalObject (oCaster, "AI_ALLY_TARGET_1" , oCaster); + SetLocalObject (oCaster, "AI_ALLY_TARGET_2", oCaster); + int nCntr = 1; + object oCreature = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, oCaster, nCntr); + if(AI_DEBUG) ai_Debug("0i_spells", "864", GetName(oCreature) + " nCntr: " + IntToString(nCntr) + + " Distance: " + FloatToString(GetDistanceBetween(oCaster, oCreature), 0, 2)); + while(oCreature != OBJECT_INVALID && nCntr < 8 && GetDistanceBetween(oCaster, oCreature) < AI_RANGE_CLOSE) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1133", "Setting " + GetName(oCreature) + " as AI_ALLY_TARGET_" + IntToString(nCntr + 2)); + SetLocalObject (oCaster, "AI_ALLY_TARGET_" + IntToString(nCntr + 2), oCreature); + oCreature = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, oCaster, ++nCntr); + if(AI_DEBUG) ai_Debug("0i_spells", "1136", GetName(oCreature) + " nCntr: " + IntToString(nCntr) + + " Distance: " + FloatToString(GetDistanceBetween(oCaster, oCreature), 0, 2)); + } +} +void ai_SetupAllyTargets(object oCaster, object oPC) +{ + // Setup our targets. + int nTarget; + if(oCaster != oPC) SetLocalObject (oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oPC); + SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCaster); + object oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); + int nCntr = 1; + int nMaxHenchman = GetMaxHenchmen() + nTarget; + object oHenchman = GetHenchman(oPC, nCntr); + while(oHenchman != OBJECT_INVALID && nCntr <= nMaxHenchman) + { + if(oHenchman == OBJECT_INVALID) break; + if(oHenchman != oCaster) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oHenchman); + oHenchman = GetHenchman(oPC, ++nCntr); + } + nCntr = 1; + while(nCntr <= nMaxHenchman) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1166", "AI_ALLY_TARGET_" + IntToString(nCntr) + ": " + + GetName(GetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(nCntr)))); + nCntr++; + } +} +void ai_SetupAllyHealingTargets(object oCaster, object oPC) +{ + int nMaxHenchman = 1; + if(oPC == OBJECT_INVALID) oPC = oCaster; + if(ai_GetAIMode(oCaster, AI_MODE_PARTY_HEALING_OFF)) + { + if(!ai_GetAIMode(oCaster, AI_MODE_SELF_HEALING_OFF)) SetLocalObject(oCaster, "AI_ALLY_HEAL_1", oCaster); + } + else + { + int nTarget; + if(oCaster != oPC) + { + SetLocalObject (oCaster, "AI_ALLY_HEAL_1", oPC); + nTarget++; + } + if(!ai_GetAIMode(oCaster, AI_MODE_SELF_HEALING_OFF)) + { + SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCaster); + } + object oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oCaster); + if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); + int nCntr = 1; + nMaxHenchman = GetMaxHenchmen() + nTarget; + object oHenchman = GetHenchman(oPC, nCntr); + while(oHenchman != OBJECT_INVALID && nTarget <= nMaxHenchman) + { + if(oHenchman == OBJECT_INVALID) break; + if(oHenchman != oCaster) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oHenchman); + oHenchman = GetHenchman(oPC, ++nCntr); + } + } + int nCntr = 1; + while(nCntr <= nMaxHenchman) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1211", "AI_ALLY_HEAL_" + IntToString(nCntr) + ": " + + GetName(GetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(nCntr++)))); + } +} +void ai_ClearBuffTargets(object oCaster, string sVariable) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1216", GetName(oCaster) + " is clearing " + sVariable + " targets."); + int nIndex; + int nMaxTargets = GetMaxHenchmen() + 6; + for(nIndex = 1; nIndex < nMaxTargets; nIndex++) + { + DeleteLocalObject (oCaster, sVariable + IntToString(nIndex)); + } +} +void ai_CheckForPerDayProperties(object oCreature, object oItem, int nBuffType, int bEquiped = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1150", "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; + int nPerDay, nCharges, nUses, nSpellBuffDuration; + int nIprpSubType, nSpell, nLevel, nIPType, nIndex; + object oTarget; + 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)) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1163", "ItempropertyType(15): " + IntToString(GetItemPropertyType(ipProp))); + nIPType = GetItemPropertyType(ipProp); + if(nIPType == ITEM_PROPERTY_CAST_SPELL) + { + // Get how they use the item (charges or uses per day). + nUses = GetItemPropertyCostTableValue(ipProp); + // We only check uses per day. + if(AI_DEBUG) ai_Debug("0i_spells", "1172", "Item uses: " + IntToString(nPerDay)); + if(nUses > 7 && nUses < 13) + { + nPerDay = GetItemPropertyUsesPerDayRemaining(oItem, ipProp); + if(AI_DEBUG) ai_Debug("0i_spells", "1176", "Item uses per day: " + IntToString(nPerDay)); + if(nPerDay > 0) + { + // SubType is the ip spell index for iprp_spells.2da + nIprpSubType = GetItemPropertySubType(ipProp); + nSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nIprpSubType)); + nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); + if(AI_DEBUG) ai_Debug("0i_spells", "1183", "nSpell: " + IntToString(nSpell) + + " nBuffType: " + IntToString(nBuffType) + + " nSpellBuffDuration: " + IntToString(nSpellBuffDuration)); + if(nBuffType == nSpellBuffDuration || nBuffType == 1) + { + oTarget = ai_GetBuffTarget(oCreature, nSpell); + if(oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1190", GetName(oCreature) + " is using" + + GetName(oItem) + " to cast " + IntToString(nSpell) + + " on " + GetName(oTarget)); + ActionUseItemOnObject(oItem, ipProp, oTarget); + } + } + } + } + } + ipProp = GetNextItemProperty(oItem); + } +} +void ai_CheckForPerDayItems(object oCreature, object oPC, int nBuffType) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1198", GetName(oCreature) + ": Checking items for per day buffs."); + if(!ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC_ITEMS)) + { + int bEquiped; + 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", "1211", GetName(oItem) + " requires " + Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)) + " slots."); + if(sSlots == "0x00000") ai_CheckForPerDayProperties(oCreature, oItem, nBuffType); + } + oItem = GetNextItemInInventory(oCreature); + } + int nSlot; + // Cycle through all the creatures equiped items. + oItem = GetItemInSlot(nSlot, oCreature); + while(nSlot < 11) + { + if(oItem != OBJECT_INVALID) ai_CheckForPerDayProperties(oCreature, oItem, nBuffType, TRUE); + oItem = GetItemInSlot(++nSlot, oCreature); + } + oItem = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature); + if(oItem != OBJECT_SELF) ai_CheckForPerDayProperties(oCreature, oItem, nBuffType, TRUE); + } + // Clean up our variables. Must be done here since these are actions! + int nCntr; + object oTarget; + while(nCntr < 11) + { + oTarget = GetLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nCntr)); + if(oTarget != OBJECT_INVALID) + { + ai_ClearSpellsCastGroups(oTarget); + DeleteLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nCntr)); + } + nCntr++; + } +} +void ai_CheckForBuffSpells(struct stSpell stSpell) +{ + ai_SetupAllyTargets(stSpell.oCaster, 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; + while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); + if(AI_DEBUG) ai_Debug("0i_spells", "1208", "nClass: " + IntToString(stSpell.nClass)); + if(stSpell.nClass == CLASS_TYPE_INVALID) break; + if(AI_DEBUG) ai_Debug("0i_spells", "1210", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; + if(AI_DEBUG) ai_Debug("0i_spells", "1214", "Memorizes Spells: " + Get2DAString("classes", "MemorizesSpells", stSpell.nClass)); + if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); + return; + } + else + { + stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); + return; + } + } + stSpell.nPosition++; + } + ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); +} +void ai_ActionCastMemorizedSummons(struct stSpell stSpell) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1122", "Start of ActionCastMemorizedSummons!"); + int nSpell; + string sBuffGroup, sBuffTarget; + object oTarget; + while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + //ai_Debug("0i_spells", "1128", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + //ai_Debug("0i_spells", "1131", "nLevel: " + IntToString(stSpell.nLevel)); + while(stSpell.nLevel > -1) + { + //ai_Debug("0i_spells", "1134", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + + // " nSlots: " + IntToString(stSpell.nSlot)); + while(stSpell.nSlot < stSpell.nMaxSlots) + { + //ai_Debug("0i_spells", "1238", "Ready: " + IntToString(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot))); + if(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot)) + { + nSpell = GetMemorizedSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); + //ai_Debug("0i_spells", "1142", "nSpell: " + IntToString(nSpell)); + 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); + 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))); + return; + } + } + stSpell.nSlot++; + } + stSpell.nLevel--; + //ai_Debug("0i_spells", "1153", "nLevel: " + IntToString(stSpell.nLevel)); + if(stSpell.nLevel > -1) + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + stSpell.nSlot = 0; + } + } + } + stSpell.nPosition++; + stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); + //ai_Debug("0i_spells", "1164", "nClass: " + IntToString(stSpell.nClass)); + if(stSpell.nClass == CLASS_TYPE_INVALID) break; + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; + stSpell.nSlot = 0; + if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + } + else + { + stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); + return; + } + } + } + ai_CheckForBuffSpells(stSpell); +} +void ai_ActionCastKnownSummons(struct stSpell stSpell) +{ + //ai_Debug("0i_spells", "1184", "Start of ActionCastKnownSummons!"); + int nSpell; + string sBuffGroup, sBuffTarget; + object oTarget; + while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + //ai_Debug("0i_spells", "1190", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + //ai_Debug("0i_spells", "1193", "nLevel: " + IntToString(stSpell.nLevel)); + while(stSpell.nLevel > -1) + { + if(stSpell.nMaxSlots) + { + //ai_Debug("0i_spells", "1198", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + + // " nSlots: " + IntToString(stSpell.nSlot)); + while(stSpell.nSlot < stSpell.nMaxSlots) + { + nSpell = GetKnownSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); + //ai_Debug("0i_spells", "1203", "Ready: " + IntToString(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell))); + if(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell)) + { + if(Get2DAString("ai_spells", "Category", nSpell) == "S") + { + 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); + 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; + ai_SetupAllyTargets(stSpell.oCaster, stSpell.oPC); + DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell))); + return; + } + } + stSpell.nSlot++; + } + } + stSpell.nLevel--; + //ai_Debug("0i_spells", "1218", "nLevel: " + IntToString(stSpell.nLevel)); + if(stSpell.nLevel > -1) + { + stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + stSpell.nSlot = 0; + } + } + } + stSpell.nPosition++; + stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); + if(stSpell.nClass == CLASS_TYPE_INVALID) break; + //ai_Debug("0i_spells", "1229", "nClass: " + IntToString(stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; + stSpell.nSlot = 0; + if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); + return; + } + else stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + } + } + ai_CheckForBuffSpells(stSpell); +} +void ai_ActionCastMemorizedBuff(struct stSpell stSpell) +{ + int nSpell; + string sBuffGroup, sBuffTarget; + object oTarget; + while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + ai_Debug("0i_spells", "1252", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + ai_Debug("0i_spells", "1255", "nLevel: " + IntToString(stSpell.nLevel)); + while(stSpell.nLevel > -1) + { + ai_Debug("0i_spells", "1258", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + + " nSlots: " + IntToString(stSpell.nSlot)); + while(stSpell.nSlot < stSpell.nMaxSlots) + { + ai_Debug("0i_spells", "1262", "Ready: " + IntToString(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot))); + if(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot)) + { + nSpell = GetMemorizedSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); + int nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); + ai_Debug("0i_spells", "1267", "nBuffType: " + IntToString(stSpell.nBuffType) + + " nSpellBuffDuration: " + IntToString(nSpellBuffDuration) + + " sBuffGroup: " + Get2DAString("ai_spells", "Buff_Group", nSpell)); + if(stSpell.nBuffType == nSpellBuffDuration || stSpell.nBuffType == 1) + { + if(stSpell.nTarget > 0) + { + sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell); + oTarget = GetLocalObject(stSpell.oCaster, "AI_ALLY_TARGET_" + IntToString(stSpell.nTarget)); + if(sBuffTarget != "0" || (sBuffTarget == "0" && stSpell.oCaster == oTarget)) + { + sBuffGroup = "AI_USED_SPELL_GROUP_" + Get2DAString("ai_spells", "Buff_Group", nSpell); + if(!ai_SpellGroupNotCast(oTarget, sBuffGroup)) oTarget == OBJECT_INVALID; + } + else oTarget == OBJECT_INVALID; + } + else oTarget = ai_GetBuffTarget(stSpell.oCaster, nSpell); + ai_Debug("0i_spells", "1284", "nSpell: " + IntToString(nSpell) + + " oTarget: " + GetName(oTarget)); + if(oTarget != OBJECT_INVALID) + { + ai_CastMemorizedSpell(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot, oTarget, TRUE, stSpell.oPC); + stSpell.nSlot++; + DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell))); + return; + } + } + } + stSpell.nSlot++; + } + stSpell.nLevel--; + ai_Debug("0i_spells", "1298", "nLevel: " + IntToString(stSpell.nLevel)); + if(stSpell.nLevel > -1) + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + stSpell.nSlot = 0; + } + } + } + stSpell.nPosition++; + stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); + if(stSpell.nClass == CLASS_TYPE_INVALID) break; + ai_Debug("0i_spells", "1309", "nClass: " + IntToString(stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; + stSpell.nSlot = 0; + if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + } + else + { + stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); + return; + } + } + } + ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); +} +void ai_ActionCastKnownBuff(struct stSpell stSpell) +{ + int nSpell; + string sBuffGroup, sBuffTarget; + object oTarget; + while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + //ai_Debug("0i_spells", "1347", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + //ai_Debug("0i_spells", "1350", "nLevel: " + IntToString(stSpell.nLevel)); + while(stSpell.nLevel > -1) + { + if(stSpell.nMaxSlots) + { + //ai_Debug("0i_spells", "1356", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + + // " nSlots: " + IntToString(stSpell.nSlot)); + while(stSpell.nSlot < stSpell.nMaxSlots) + { + nSpell = GetKnownSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); + int nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); + //ai_Debug("0i_spells", "1361", "nBuffType: " + IntToString(stSpell.nBuffType) + + // " nSpellBuffDuration: " + IntToString(nSpellBuffDuration) + + // " sBuffGroup: " + Get2DAString("ai_spells", "Buff_Group", nSpell)); + if(stSpell.nBuffType == nSpellBuffDuration || stSpell.nBuffType == 1) + { + //ai_Debug("0i_spells", "1367", "Ready: " + IntToString(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell))); + if(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell)) + { + if(stSpell.nTarget > 0) + { + sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell); + oTarget = GetLocalObject(stSpell.oCaster, "AI_ALLY_TARGET_" + IntToString(stSpell.nTarget)); + if(sBuffTarget != "0" || (sBuffTarget == "0" && stSpell.oCaster == oTarget)) + { + sBuffGroup = "AI_USED_SPELL_GROUP_" + Get2DAString("ai_spells", "Buff_Group", nSpell); + if(!ai_SpellGroupNotCast(oTarget, sBuffGroup)) oTarget == OBJECT_INVALID; + } + else oTarget == OBJECT_INVALID; + } + else oTarget = ai_GetBuffTarget(stSpell.oCaster, nSpell); + //ai_Debug("0i_spells", "1382", "nSpell: " + IntToString(nSpell) + + // " oTarget: " + GetName(oTarget)); + if(oTarget != OBJECT_INVALID) + { + ai_CastKnownSpell(stSpell.oCaster, stSpell.nClass, nSpell, oTarget, TRUE, stSpell.oPC); + stSpell.nSlot++; + DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell))); + return; + } + } + } + stSpell.nSlot++; + } + } + stSpell.nLevel--; + //ai_Debug("0i_spells", "1396", "nLevel: " + IntToString(stSpell.nLevel)); + if(stSpell.nLevel > -1) + { + stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + stSpell.nSlot = 0; + } + } + } + stSpell.nPosition++; + stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); + if(stSpell.nClass == CLASS_TYPE_INVALID) break; + //ai_Debug("0i_spells", "921", "nClass: " + IntToString(stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; + stSpell.nSlot = 0; + if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); + return; + } + else stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + } + } + ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); +} +void ai_CastBuffs(object oCaster, int nBuffType, int nTarget, object oPC) +{ + // buff types: 1 - All, 2 - Short duration, 3 - Long duration + // Buff groups are used to prevent a henchmen to cast spells that have the same effect, + // for example: resist elements and protection from elements are similiar so the henchmen + // would cast only the most powerful among these if he has them both. + if(AI_DEBUG) ai_Debug("0i_spells", "1670", GetName(oCaster) + " is casting buffs: " + IntToString(nBuffType) + + " nTarget: " + IntToString(nTarget) + "!"); + struct stSpell stSpell; + stSpell.oPC = oPC; + stSpell.oCaster = oCaster; + stSpell.nBuffType = nBuffType; + stSpell.nTarget = nTarget; + stSpell.nPosition = 1; + // Look for summons spells on All, Long durations and the whole party. + if((nBuffType == 1 || nBuffType == 3) && nTarget == 0) + { + while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); + if(AI_DEBUG) ai_Debug("0i_spells", "1684", "nClass: " + IntToString(stSpell.nClass)); + if(stSpell.nClass == CLASS_TYPE_INVALID) break; + if(AI_DEBUG) ai_Debug("0i_spells", "1686", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); + if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") + { + stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; + if(AI_DEBUG) ai_Debug("0i_spells", "1692", "MemorizesSpells: " + Get2DAString("classes", "MemorizesSpells", stSpell.nClass)); + if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") + { + stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedSummons(stSpell)); + return; + } + else + { + stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); + AssignCommand(stSpell.oCaster, ai_ActionCastKnownSummons(stSpell)); + return; + } + } + stSpell.nPosition++; + } + // 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); +} +int ai_CastSpontaneousCure(object oCreature, object oTarget, object oPC) +{ + if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) return FALSE; + if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_SPONTANEOUS_CURE)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_spells", "1643", GetName(oCreature) + " is looking to cast a spontaneous cure spell."); + if(!GetLevelByClass(CLASS_TYPE_CLERIC, oCreature)) return FALSE; + int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); + int nSpell, nSlot, nMaxSlots, nLevel = 4; + int nSpellSave, nSlotSave, nLevelSave = 5; + string sSpellName; + while(nLevel > -1) + { + // We check CLASS_TYPE_CLERIC as thats the only class with spontaneous cure spells. + nMaxSlots = GetMemorizedSpellCountByLevel(oCreature, CLASS_TYPE_CLERIC, nLevel); + nSlot = 0; + if(AI_DEBUG) ai_Debug("0i_spells", "1653", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); + while(nSlot < nMaxSlots) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1656", "nSlot: " + IntToString(nSlot) + + " Spell Ready: " + IntToString(GetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevel, nSlot))); + if(GetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevel, nSlot)) + { + if(nLevel == 4) nSpell = SPELL_CURE_CRITICAL_WOUNDS; + else if(nLevel == 3) nSpell = SPELL_CURE_SERIOUS_WOUNDS; + else if(nLevel == 2) nSpell = SPELL_CURE_MODERATE_WOUNDS; + else if(nLevel == 1) nSpell = SPELL_CURE_LIGHT_WOUNDS; + else nSpell = 0; + if(AI_DEBUG) ai_Debug("0i_spells", "1665", "nSpell: " + IntToString(nSpell)); + if(nSpell) + { + if(ai_ShouldWeCastThisCureSpell(nSpell, nDamage)) + { + SetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevel, nSlot, FALSE); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + ai_SendMessages(GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_MAGENTA, oPC); + if(AI_DEBUG) ai_Debug("0i_spells", "1673", GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + "."); + ActionCastSpellAtObject(nSpell, oTarget, 255, TRUE); + return TRUE; + } + // Save the lowest level cure spell as we might need to cast it. + else if(nLevel < nLevelSave) + { + nSpellSave = nSpell; + nLevelSave = nLevel; + nSlotSave = nSlot; + } + } + } + nSlot++; + } + nLevel--; + } + // Did we find a cure spell? If we did then use it. + if(nSpellSave) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1693", GetName(oCreature) + " has cast the lowest level cure spell on " + GetName(oTarget) + "."); + SetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevelSave, nSlotSave, FALSE); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpellSave))); + ai_SendMessages(GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_MAGENTA, oPC); + ActionCastSpellAtObject(nSpellSave, oTarget, 255, TRUE); + return TRUE; + } + return FALSE; +} +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."); + 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", "1710", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); + while(nSlot < nMaxSlots) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1713", "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_ShouldWeCastThisCureSpell(nSpell, nDamage)) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(AI_DEBUG) ai_Debug("0i_spells", "1721", GetName(oCreature) + " has cast " + sSpellName + " on " + GetName(oTarget) + "."); + ai_CastMemorizedSpell(oCreature, nClass, nLevel, nSlot, oTarget, FALSE, oPC); + return TRUE; + } + // Save the lowest level cure spell as we might need to cast it. + else if(nLevel < nLevelSave && (nSpell > 26 && nSpell < 32)) + { + 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_CastKnownHealing(object oCreature, object oTarget, object oPC, int nClass) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1748", GetName(oCreature) + " is looking to cast a known cure 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", "1756", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); + while(nSlot < nMaxSlots) + { + nSpell = GetKnownSpellId(oCreature, nClass, nLevel, nSlot); + if(AI_DEBUG) ai_Debug("0i_spells", "1760", "nSlot: " + IntToString(nSlot) + + " Spell Ready: " + IntToString(GetSpellUsesLeft(oCreature, nClass, nSpell))); + if(GetSpellUsesLeft(oCreature, nClass, nSpell)) + { + if(ai_ShouldWeCastThisCureSpell(nSpell, nDamage)) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(AI_DEBUG) ai_Debug("0i_spells", "1767", 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 > 26 && nSpell < 32)) + { + 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_ConcentrationCondition(object oCreature) +{ + int nType; + effect eEffect = GetFirstEffect(oCreature); + while(GetIsEffectValid(eEffect)) + { + nType = GetEffectType(eEffect); + if(nType == EFFECT_TYPE_STUNNED || nType == EFFECT_TYPE_PARALYZE || + nType == EFFECT_TYPE_SLEEP || nType == EFFECT_TYPE_FRIGHTENED || + nType == EFFECT_TYPE_PETRIFY || nType == EFFECT_TYPE_CONFUSED || + nType == EFFECT_TYPE_DOMINATED || nType == EFFECT_TYPE_POLYMORPH) + { + return TRUE; + } + eEffect = GetNextEffect(oCreature); + } + return FALSE; +} +void ai_SpellConcentrationCheck(object oCaster = OBJECT_SELF) +{ + object oMaster = GetMaster(); + if(GetLocalInt(oCaster,"X2_L_CREATURE_NEEDS_CONCENTRATION")) + { + if(GetIsObjectValid(oMaster)) + { + int nAction = GetCurrentAction(oMaster); + // master doing anything that requires attention and breaks concentration + if(nAction == ACTION_DISABLETRAP || nAction == ACTION_TAUNT || + nAction == ACTION_PICKPOCKET || nAction ==ACTION_ATTACKOBJECT || + nAction == ACTION_COUNTERSPELL || nAction == ACTION_FLAGTRAP || + nAction == ACTION_CASTSPELL || nAction == ACTION_ITEMCASTSPELL) + { + SignalEvent(oCaster,EventUserDefined(X2_EVENT_CONCENTRATION_BROKEN)); + } + else if(ai_ConcentrationCondition(oMaster)) + { + SignalEvent(oCaster,EventUserDefined(X2_EVENT_CONCENTRATION_BROKEN)); + } + } + } +} +int ai_CastInMelee(object oCreature, int nSpell, int nInMelee) +{ + // If this is a spell and we are in melee. + if(nInMelee > 0 && !GetHasFeat(FEAT_EPIC_IMPROVED_COMBAT_CASTING, oCreature)) + { + // Using DC 19 so we will use with up to a 50% failure. + int nSpellLevel = StringToInt(Get2DAString("spells", "Innate", nSpell)); + int nDC = AI_DEFENSIVE_CASTING_DC + nSpellLevel; + int nRoll = Random(AI_DEFENSIVE_CASTING_DIE) + 1; + int nConcentration = GetSkillRank(SKILL_CONCENTRATION, oCreature); + if(GetHasFeat(FEAT_COMBAT_CASTING, oCreature)) nConcentration += 4; + if(AI_DEBUG) ai_Debug("0i_spells", "1081", "Use Defensive Casting? nDC: " + IntToString(nDC) + " FEAT_COMBAT_CASTING: " + + IntToString(GetHasFeat(FEAT_COMBAT_CASTING, oCreature)) + + " nConcentration: " + IntToString(nConcentration) + " + nRoll: " + IntToString(nRoll)); + if(nConcentration + nRoll > nDC) + { + if(AI_DEBUG) ai_Debug("0i_spells", "1086", GetName(oCreature) + " is casting defensively!"); + SetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST, TRUE); + } + // Defensive casting is a bad idea so maybe casting anyspell is a bad idea. + else + { + object oMelee = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + if(GetIsObjectValid(oMelee)) + { + nRoll = Random(AI_CASTING_IN_MELEE_ROLL) + 1; + nDC = AI_CASTING_IN_MELEE_DC + nSpellLevel + nInMelee * ai_GetCreatureAttackBonus(oMelee); + if(AI_DEBUG) ai_Debug("0i_spells", "1097", "Cast anyway: nConcentration: " + IntToString(nConcentration) + + " nRoll: " + IntToString(nRoll) + " nDC: " + IntToString(nDC) + + " oMelee: " + GetName(oMelee)); + if(nConcentration + nRoll > nDC) return TRUE; + if(AI_DEBUG) ai_Debug("0i_spells", "1101", GetName(oCreature) + " is not casting in melee against " + GetName(oMelee)); + return FALSE; + } + } + } + // We don't need to cast defensively so lets make sure it's off. + else if(GetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST)) + { + SetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST, FALSE); + } + return TRUE; +} +float ai_GetOffensiveSpellSearchRange(object oCreature, int nSpell) +{ + // Search the spell range + the distance to the closest enemy - 7.5 meters). + // This will keep the caster from running up on an enemy to cast. + // But allow them to move up some if needed. + float fRange = ai_GetSpellRange(nSpell); + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + float fEnemyDistance = GetDistanceBetween(oCreature, oNearestEnemy); + // Spell range is less than the nearest enemy. Restrict based on nearest enemy. + // Spell range is less than the nearestenemy. Check enemy action then adjust. + if(fRange < fEnemyDistance) + { + // 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(nAction != ACTION_MOVETOPOINT || nAction != ACTION_ITEMCASTSPELL || + nAction != ACTION_INVALID || nAction != ACTION_USEOBJECT || + nAction != ACTION_RANDOMWALK) fRange = fEnemyDistance + (fRange - 7.5); + } + if(fRange > AI_RANGE_BATTLEFIELD) return AI_RANGE_BATTLEFIELD; + else if(fRange < 0.1f) return 0.1f; + return fRange; +} +int ai_ShouldWeCastThisCureSpell(int nSpell, int nDamage) +{ + if(AI_DEBUG) ai_Debug("0i_spells", "1127", "nSpell: " + IntToString(nSpell) + " nDamage: " + + IntToString(nDamage)); + if(nSpell == SPELL_HEAL && nDamage > 50) return TRUE; + else if(nSpell == SPELL_CURE_CRITICAL_WOUNDS && nDamage > 31) return TRUE; + else if(nSpell == SPELL_CURE_SERIOUS_WOUNDS && nDamage > 23) return TRUE; + else if(nSpell == SPELL_CURE_MODERATE_WOUNDS && nDamage > 15) return TRUE; + else if(nSpell == SPELL_CURE_LIGHT_WOUNDS && nDamage > 6) return TRUE; + else if(nSpell == SPELL_CURE_MINOR_WOUNDS) return TRUE; + return FALSE; +} +void ai_CastWidgetSpell(object oPC, object oAssociate, object oTarget, location lLocation) +{ + int nIndex = GetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); + DeleteLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + json jSpell = JsonArrayGet(jWidget, nIndex); + int nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + int nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + int nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); + int nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); + //SendMessageToPC(oPC, "nSpell: " + IntToString(nSpell) + + // " oTarget: " + GetName(oTarget) + + // " nMetaMagic: " + IntToString(nMetaMagic) + + // " nDomain: " + IntToString(nDomain)); + if(GetCurrentAction(oAssociate) != ACTION_CASTSPELL) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); + if(!GetIsObjectValid(oTarget)) + { + AssignCommand(oAssociate, ActionCastSpellAtLocation(nSpell, lLocation, nMetaMagic, FALSE, 0, FALSE, -1, FALSE, nDomain)); + } + else AssignCommand(oAssociate, ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain)); + +} +void ai_UseWidgetFeat(object oPC, object oAssociate, object oTarget, location lLocation) +{ + int nIndex = GetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); + DeleteLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + json jFeat = JsonArrayGet(jWidget, nIndex); + int nFeat = JsonGetInt(JsonArrayGet(jFeat, 5)); + 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(TRUE)); + //SendMessageToPC(oPC, "0i_spells, 2104, nFeat: " + IntToString(nFeat) + " oTarget: " + GetName(oTarget)); + if(!GetIsObjectValid(oTarget)) + { + AssignCommand(oAssociate, ActionUseFeat(nFeat, OBJECT_INVALID, nSubSpell, lLocation)); + } + else AssignCommand(oAssociate, ActionUseFeat(nFeat, oTarget, nSubSpell)); +} +void ai_UseWidgetItem(object oPC, object oAssociate, object oTarget, location lLocation) +{ + int nIndex = GetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); + DeleteLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); + string sAssociateType = ai_GetAssociateType(oPC, oAssociate); + json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); + json jSpells = JsonArrayGet(jAIData, 10); + json jWidget = JsonArrayGet(jSpells, 2); + json jItem = JsonArrayGet(jWidget, nIndex); + int nSpell = JsonGetInt(JsonArrayGet(jItem, 0)); + 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(nSpell == SPELL_HEALINGKIT) + { + ipProperty = GetFirstItemProperty(oItem); + if(GetItemPropertyType(ipProperty) == ITEM_PROPERTY_HEALERS_KIT) + { + if(ai_GetIsCharacter(oPC)) ai_SendMessages(GetName(oAssociate) + " uses " + GetName(oItem) + " on " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); + AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oTarget)); + return; + } + } + ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + if(nIprpSubType == GetItemPropertySubType(ipProperty)) break; + ipProperty = GetNextItemProperty(oItem); + } + if(!GetIsObjectValid(oTarget)) + { + AssignCommand(oAssociate, ActionUseItemAtLocation(oItem, ipProperty, lLocation)); + } + else AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oTarget)); +} diff --git a/_module/nss/0i_states_cond.nss b/_module/nss/0i_states_cond.nss new file mode 100644 index 00000000..4c77ccf0 --- /dev/null +++ b/_module/nss/0i_states_cond.nss @@ -0,0 +1,423 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_states_cond +////////////////////////////////////////////////////////////////////////////////////////////////////// + Include scripts that handle states and conditions for combat. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_main" +#include "0i_messages" +#include "0i_time" +//#include "X0_I0_COMBAT" +// Wrapper for ClearAllActions - we have added extra vars to be cleared as well. +// Note this references OBJECT_SELF! +void ai_ClearCreatureActions(int bClearCombatState = FALSE); +// Used in combat to keep track of the creatures last rounds action. +// One use is to make sure we don't use the same spell on the next round. +// 0+ is the spell that was cast, other actions use AI_LAST_ACTION_* constants. +void ai_SetLastAction(object oCreature, int nAction = AI_LAST_ACTION_NONE); +// Returns TRUE if oCreatures last rounds action is equal to nAction. +// 0+ is the spell that was cast, other actions use AI_LAST_ACTION_* constants. +int ai_CompareLastAction(object oCreature, int nAction); +// Sets the correct listen checks on oCreature. +void ai_SetListeningPatterns(object oCreature); +// Returns TRUE if oCreature is an elemental, undead, or golem i.e. non-living. +int ai_IsNonliving(int nRacialType); +// Returns TRUE if oCreature is in combat. +int ai_GetIsInCombat(object oCreature); +// Sets the time that this oCreature's current combat round started. +// Using action based combat rounds has an unfortunate side effect: +// Once you attack in melee you will continue to attack in melee do to hardcoded +// logic. This will "PUSH" your end of round back until it decides to stop attacking! +// We avoid this by setting the time and if we check for combat and 6 seconds has +// passed then we assume the current round is over, ClearAllActions, and start the next round. +void ai_SetCombatRound(object oCreature); +// Clears the current combat round timer by deleting the value. +void ai_EndCombatRound(object oCreature); +// Returns TRUE if AI_COMBAT_ROUND_IN_SECONDS has not passed since ai_SetCombatRound. +// If it returns FALSE then it will clear the current combat round timer. +int ai_IsInCombatRound(object oCreature, int nCombatRound = AI_COMBAT_ROUND_IN_SECONDS); +// Returns TRUE if oCreature is busy. +// This checks various actions to see if oCreature is busy; +// in combat, busy mode, Actions: attacking, casting spell, counterspelling, +// disabling trap, item casting spell, opening lock, resting, setting trap. +int ai_GetIsBusy(object oCreature); +// Returns a value based on the disabling effect. +// Dead = 1, Bleeding = 2, Dying = 2, Stunned = 29, Confused = 24, Paralyzed = 27 +// Frightened 25, Turned = 35, Petrified = 79, Charmed = 23, Disappearappear = 75, +// Time Stop = 66, Dazed = 28, Sleep = 30. +// Returns FALSE if not Disabled. +int ai_Disabled(object oCreature); +// Set one of the AI_MODE_* bitwise constants on oAssociate to bOn. +void ai_SetAIMode(object oAssociate, int nBit, int bOn = TRUE); +// Return if nMode is set on oAssociate. Uses the AI_MODE_* bitwise constants. +int ai_GetAIMode(object oAssociate, int nBit); +// Set one of the AI_MAGIC_* bitwise constants on oAssociate to bOn. +void ai_SetMagicMode(object oAssociate, int nBit, int bOn = TRUE); +// Return if nMode is set on oAssociate. Uses the AI_MAGIC_* bitwise constants. +int ai_GetMagicMode(object oAssociate, int nBit); +// This is based off of the PC's settings for an associate and other creatures use a default. +// Set one of the AI_LOOT_* bitwise constants on oAssociate to bOn. +void ai_SetLootFilter(object oAssociate, int nBit, int bOn = TRUE); +// Return if nMode is set on oAssociate. Uses the AI_LOOT_* bitwise constants. +int ai_GetLootFilter(object oAssociate, int nBit); +// Set one of the AI_IP_* bitwise constants on oCreature to bOn. +void ai_SetItemProperty(object oCreature, string sVarname, int nBit, int bOn = TRUE); +// Return if nMode is set on oCreature. Uses the AI_IP_* bitwise constants. +int ai_GetItemProperty(object oCreature, string sVarname, int nBit); +// Returns the number of hitpoints a creature must have to not be healed. +// This is based off of the PC's settings for an associate and other creatures use a default. +int ai_GetHealersHpLimit(object oCreature, int bInCombat = TRUE); +// Returns TRUE if nCondition is within nCurrentConditions. +// nCurrentConditions is setup in ai_GetNegativeConditions. +int ai_GetHasNegativeCondition(int nCondition, int nCurrentConditions); +// Returns an integer with bitwise flags set that represent the current negative +// conditions on oCreature. ai_GetHasNegativeCondition uses this function. +int ai_GetNegativeConditions(object oCreature); +// Returns TRUE if oObject is in the line of sight of oCreature. +// If the creature is close LineOfSight doesn't work well. +int ai_GetIsInLineOfSight(object oCreature, object oObject); +// Add the specified condition flag to the behavior state of the caller +void ai_SetBehaviorState(int nCondition, int bValid = TRUE); +// Returns TRUE if the specified behavior flag is set on the caller +int ai_GetBehaviorState(int nCondition); +// Highlights the current mode for the widget passed. +void ai_HighlightWidgetMode(object oPC, object oAssociate, int nToken); +// Checks to see if the party scale is correctly adjusted. +void ai_CheckXPPartyScale(object oCreature); + +void ai_ClearCreatureActions(int bClearCombatState = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_states_cond", "89", GetName(OBJECT_SELF) + " is clearing actions (" + + IntToString(bClearCombatState) + ")!"); + DeleteLocalInt(OBJECT_SELF, AI_CURRENT_ACTION_MODE); + ClearAllActions(bClearCombatState); +} +void ai_SetLastAction(object oCreature, int nAction = AI_LAST_ACTION_NONE) +{ + SetLocalInt(oCreature, sLastActionVarname, nAction); +} +int ai_CompareLastAction(object oCreature, int nAction) +{ + // Are we checking to see if we cast a spell? + if(nAction == AI_LAST_ACTION_CAST_SPELL && + GetLocalInt(oCreature, sLastActionVarname) > -1) return TRUE; + // Check other last actions. + return (nAction == GetLocalInt(oCreature, sLastActionVarname)); +} +void ai_SetListeningPatterns(object oCreature) +{ + 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); + SetListenPattern(oCreature, AI_ATKED_BY_SPELL, AI_ALLY_ATKED_BY_SPELL); + SetListenPattern(oCreature, AI_I_AM_WOUNDED, AI_ALLY_IS_WOUNDED); + SetListenPattern(oCreature, AI_I_AM_DEAD, AI_ALLY_IS_DEAD); + 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) +{ + switch(nRacialType) + { + case RACIAL_TYPE_CONSTRUCT: + case RACIAL_TYPE_ELEMENTAL: + case RACIAL_TYPE_UNDEAD: return TRUE; + } + return FALSE; +} +int ai_GetIsInCombat(object oCreature) +{ + if(AI_DEBUG) ai_Debug("0i_states_cond", "110", GetName(oCreature) + " is in Combat: Enemy Numbers = " + IntToString(GetLocalInt(oCreature, AI_ENEMY_NUMBERS))); + + return GetLocalInt(oCreature, AI_ENEMY_NUMBERS); +} +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()) + " <==============="); +} +void ai_EndCombatRound(object oCreature) +{ + if(AI_DEBUG) ai_Debug("0i_states_cond", "120", " ===============> " + GetName(oCreature) + " ROUND END:" + IntToString(SQLite_GetTimeStamp()) + " <==============="); + DeleteLocalInt(oCreature, "AI_COMBAT_ROUND_START"); +} +int ai_IsInCombatRound(object oCreature, int nCombatRound = AI_COMBAT_ROUND_IN_SECONDS) +{ + int nCombatRoundStart = GetLocalInt(oCreature, "AI_COMBAT_ROUND_START"); + 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) + ")"); + if(nCombatRoundTime < nCombatRound) return TRUE; + ai_EndCombatRound(oCreature); + return FALSE; +} +// Testing to see if we can fix some delaying in combat. +int ai_GetIsBusy(object oCreature) +{ + int nAction = GetCurrentAction(oCreature); + if(AI_DEBUG) ai_Debug("0i_states_cond", "140", GetName(oCreature) + " Get is Busy, action: " + + IntToString(nAction)); + switch(nAction) + { + case ACTION_CASTSPELL : + case ACTION_ITEMCASTSPELL : + case ACTION_OPENLOCK : + case ACTION_REST : + case ACTION_DISABLETRAP : + case ACTION_ATTACKOBJECT : + case ACTION_COUNTERSPELL : + case ACTION_SETTRAP : return TRUE; + case ACTION_WAIT : + case ACTION_INVALID : + { + int nCombatWait = GetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + if(AI_DEBUG) ai_Debug("0i_states_cond", "153", "nCombatWait: " + IntToString(nCombatWait) + + " AI_AM_I_SEARCHING: " + IntToString(GetLocalInt(oCreature, AI_AM_I_SEARCHING))); + if(nCombatWait) + { + if(ai_IsInCombatRound(oCreature, nCombatWait)) return TRUE; + DeleteLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + } + else if(GetLocalInt(oCreature, AI_AM_I_SEARCHING)) DeleteLocalInt(oCreature, AI_AM_I_SEARCHING); + return FALSE; + } + case ACTION_MOVETOPOINT : + { + return ai_GetIsInCombat(oCreature); + } + } + return FALSE; +} +int ai_Disabled(object oCreature) +{ + if(GetIsDead(oCreature)) return 1; + // Check for effects. + effect eEffect = GetFirstEffect(oCreature); + while(GetIsEffectValid(eEffect)) + { + switch(GetEffectType(eEffect)) + { + case EFFECT_TYPE_DOMINATED : + { + if(!GetCommandable(oCreature)) SetCommandable(TRUE, oCreature); + return FALSE; + } + case EFFECT_TYPE_STUNNED : + case EFFECT_TYPE_DAZED : + case EFFECT_TYPE_SLEEP : + case EFFECT_TYPE_CONFUSED : + case EFFECT_TYPE_FRIGHTENED : + case EFFECT_TYPE_PARALYZE : + case EFFECT_TYPE_TURNED : + case EFFECT_TYPE_CHARMED : + case EFFECT_TYPE_PETRIFY : + case EFFECT_TYPE_TIMESTOP : + { + if(AI_DEBUG) ai_Debug("0i_stats_cond", "195", GetName(oCreature) + " is disabled(" + + IntToString(GetEffectType(eEffect)) + ")"); + return GetEffectType(eEffect); + } + } + eEffect = GetNextEffect(oCreature); + } + // Not Commandable is basically disabled as far as the AI is concerned. + if(!GetCommandable(oCreature)) + { + if(AI_DEBUG) ai_Debug("0i_stats_cond", "213", GetName(oCreature) + " is disabled(Not Commandable)!"); + return EFFECT_TYPE_PARALYZE; + } + if(AI_DEBUG) ai_Debug("0i_states_cond", "202", GetName(oCreature) + " is not disabled."); + return FALSE; +} +void ai_SetAIMode(object oAssociate, int nBit, int bOn = TRUE) +{ + int nAIModes = GetLocalInt(oAssociate, sAIModeVarname); + if(bOn) nAIModes = nAIModes | nBit; + else nAIModes = nAIModes & ~nBit; + SetLocalInt(oAssociate, sAIModeVarname, nAIModes); + // Set widget to show the mode they are in. + +} +int ai_GetAIMode(object oAssociate, int nBit) +{ + if(GetLocalInt(oAssociate, sAIModeVarname) & nBit) return TRUE; + return FALSE; +} +void ai_SetMagicMode(object oAssociate, int nBit, int bOn = TRUE) +{ + int nMagicModes = GetLocalInt(oAssociate, sMagicModeVarname); + if(bOn) nMagicModes = nMagicModes | nBit; + else nMagicModes = nMagicModes & ~nBit; + SetLocalInt(oAssociate, sMagicModeVarname, nMagicModes); +} +int ai_GetMagicMode(object oAssociate, int nBit) +{ + if(GetLocalInt(oAssociate, sMagicModeVarname) & nBit) return TRUE; + return FALSE; +} +void ai_SetLootFilter(object oAssociate, int nLootBit, int bOn = TRUE) +{ + int nLootFilter = GetLocalInt(oAssociate, sLootFilterVarname); + if(bOn) nLootFilter = nLootFilter | nLootBit; + else nLootFilter = nLootFilter & ~nLootBit; + SetLocalInt(oAssociate, sLootFilterVarname, nLootFilter); +} +int ai_GetLootFilter(object oAssociate, int nBit) +{ + if(GetLocalInt(oAssociate, sLootFilterVarname) & nBit) return TRUE; + return FALSE; +} +void ai_SetItemProperty(object oCreature, string sVarname, int nBit, int bOn = TRUE) +{ + int nItemProperties = GetLocalInt(oCreature, sVarname); + if(bOn) nItemProperties = nItemProperties | nBit; + else nItemProperties = nItemProperties & ~nBit; + SetLocalInt(oCreature, sVarname, nItemProperties); +} +int ai_GetItemProperty(object oCreature, string sVarname, int nBit) +{ + if(GetLocalInt(oCreature, sVarname) & nBit) return TRUE; + return FALSE; +} +int ai_GetHealersHpLimit(object oCreature, int bInCombat = TRUE) +{ + if(bInCombat) return GetLocalInt(oCreature, AI_HEAL_IN_COMBAT_LIMIT); + else return GetLocalInt(oCreature, AI_HEAL_OUT_OF_COMBAT_LIMIT); +} +int ai_GetHasNegativeCondition(int nCondition, int nCurrentConditions) +{ + return (nCurrentConditions & nCondition); +} +int ai_GetNegativeConditions(object oCreature) +{ + int nCondition, nEffectType; + effect eEffect = GetFirstEffect(oCreature); + while(GetIsEffectValid (eEffect)) + { + // Rage and maybe other abilities might come from the oCreature! + if(GetEffectCreator(eEffect) != oCreature) + { + nEffectType = GetEffectType(eEffect); + switch(nEffectType) + { + case EFFECT_TYPE_DISEASE: nCondition = nCondition | AI_CONDITION_DISEASE; break; + case EFFECT_TYPE_POISON: nCondition = nCondition | AI_CONDITION_POISON; break; + case EFFECT_TYPE_CURSE: nCondition = nCondition | AI_CONDITION_CURSE; break; + case EFFECT_TYPE_BLINDNESS: + case EFFECT_TYPE_DEAF: nCondition = nCondition | AI_CONDITION_BLINDDEAF; break; + case EFFECT_TYPE_ABILITY_DECREASE: nCondition = nCondition | AI_CONDITION_ABILITY_DRAIN; break; + case EFFECT_TYPE_NEGATIVELEVEL: nCondition = nCondition | AI_CONDITION_LEVEL_DRAIN; break; + case EFFECT_TYPE_AC_DECREASE: nCondition = nCondition | AI_CONDITION_AC_DECREASE; break; + case EFFECT_TYPE_ATTACK_DECREASE: nCondition = nCondition | AI_CONDITION_ATK_DECREASE; break; + case EFFECT_TYPE_CHARMED: nCondition = nCondition | AI_CONDITION_CHARMED; break; + case EFFECT_TYPE_CONFUSED: nCondition = nCondition | AI_CONDITION_CONFUSED; break; + case EFFECT_TYPE_DAZED: nCondition = nCondition | AI_CONDITION_DAZED; break; + case EFFECT_TYPE_DAMAGE_DECREASE: nCondition = nCondition | AI_CONDITION_DMG_DECREASE; break; + case EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE: nCondition = nCondition | AI_CONDITION_DMG_I_DECREASE; break; + case EFFECT_TYPE_DOMINATED: nCondition = nCondition | AI_CONDITION_DOMINATED; break; + case EFFECT_TYPE_FRIGHTENED: nCondition = nCondition | AI_CONDITION_FRIGHTENED; break; + case EFFECT_TYPE_PARALYZE: nCondition = nCondition | AI_CONDITION_PARALYZE; break; + case EFFECT_TYPE_SAVING_THROW_DECREASE: nCondition = nCondition | AI_CONDITION_SAVE_DECREASE; break; + case EFFECT_TYPE_SKILL_DECREASE: nCondition = nCondition | AI_CONDITION_SKILL_DECREASE; break; + case EFFECT_TYPE_SLOW: nCondition = nCondition | AI_CONDITION_SLOW; break; + case EFFECT_TYPE_SPELL_RESISTANCE_DECREASE: nCondition = nCondition | AI_CONDITION_SR_DECREASE; break; + case EFFECT_TYPE_STUNNED: nCondition = nCondition | AI_CONDITION_STUNNED; break; + } + } + eEffect = GetNextEffect(oCreature); + } + return nCondition; +} +int ai_GetIsInLineOfSight(object oCreature, object oObject) +{ + // Creatures can block the line of sight so when close we shouldn't check. + if(GetDistanceBetween(oObject, oCreature) <= AI_RANGE_MELEE) return TRUE; + return LineOfSightObject(oCreature, oObject); +} +void ai_SetBehaviorState(int nCondition, int bValid = TRUE) +{ + int nPlot = GetLocalInt(OBJECT_SELF, "NW_BEHAVIOR_MASTER"); + if(bValid) + { + nPlot = nPlot | nCondition; + SetLocalInt(OBJECT_SELF, "NW_BEHAVIOR_MASTER", nPlot); + } + else + { + nPlot = nPlot & ~nCondition; + SetLocalInt(OBJECT_SELF, "NW_BEHAVIOR_MASTER", nPlot); + } +} +int ai_GetBehaviorState(int nCondition) +{ + int nPlot = GetLocalInt(OBJECT_SELF, "NW_BEHAVIOR_MASTER"); + if(nPlot & nCondition) return TRUE; + return FALSE; +} +void ai_HighlightWidgetMode(object oPC, object oAssociate, int nToken) +{ + if(oPC == oAssociate) return; + int bBool; + bBool = ai_GetAIMode(oAssociate,AI_MODE_DEFEND_MASTER); + NuiSetBind(oPC, nToken, "btn_cmd_guard_encouraged", JsonBool(bBool)); + bBool = ai_GetAIMode(oAssociate,AI_MODE_STAND_GROUND); + NuiSetBind(oPC, nToken, "btn_cmd_hold_encouraged", JsonBool(bBool)); + bBool = ai_GetAIMode(oAssociate,AI_MODE_FOLLOW); + NuiSetBind(oPC, nToken, "btn_cmd_follow_encouraged", JsonBool(bBool)); + if(!ai_GetAIMode(oAssociate, AI_MODE_DEFEND_MASTER) && + !ai_GetAIMode(oAssociate, AI_MODE_STAND_GROUND) && + !ai_GetAIMode(oAssociate, AI_MODE_FOLLOW)) bBool = TRUE; + else bBool = FALSE; + NuiSetBind(oPC, nToken, "btn_cmd_attack_encouraged", JsonBool(bBool)); +} +void ai_CheckXPPartyScale(object oCreature) +{ + object oModule = GetModule(); + if(!GetLocalInt(oModule, AI_RULE_PARTY_SCALE)) return; + object oMaster; + if(!ai_GetIsCharacter(oCreature)) + { + oMaster = GetMaster(oCreature); + while(oMaster != OBJECT_INVALID) + { + if(ai_GetIsCharacter(oMaster)) break; + oMaster = GetMaster(oMaster); + } + if(oMaster == OBJECT_INVALID) return; + } + else oMaster = oCreature; + float fDefaultXPScale = IntToFloat(GetLocalInt(oModule, AI_BASE_PARTY_SCALE_XP)); + float fPartySize = 4.0; + int nAssociateType, nHenchman, nHenchAssociate; + object oHenchman; + for(nAssociateType = 1; nAssociateType <= 5; nAssociateType++) + { + if(nAssociateType == ASSOCIATE_TYPE_HENCHMAN) + { + for(nHenchman = 1; nHenchman <= AI_MAX_HENCHMAN; nHenchman++) + { + oHenchman = GetAssociate(nAssociateType, oMaster, nHenchman); + if(oHenchman != OBJECT_INVALID) + { + fPartySize += 1.0; + for(nHenchAssociate = 2; nHenchAssociate <= 5; nHenchAssociate++) + { + if(GetAssociate(nHenchAssociate, oHenchman, 1) != OBJECT_INVALID) fPartySize += 1.0; + } + } + } + } + else if(GetAssociate(nAssociateType, oMaster, 1) != OBJECT_INVALID) fPartySize += 1.0; + } + int nXPScale = FloatToInt(fPartySize / 4.0 * fDefaultXPScale); + //SendMessageToPC(oMaster, GetName(oMaster) + " nXPScale = (3 + fPartySize / 4.0 * fDefaultXPScale)" + + // IntToString(nXPScale) + " = (" + FloatToString(fPartySize, 0, 1) + " / 4.0 * " + + // FloatToString(fDefaultXPScale, 0, 1) + ")"); + SetModuleXPScale(nXPScale); +} + diff --git a/_module/nss/0i_talents.nss b/_module/nss/0i_talents.nss new file mode 100644 index 00000000..6728abbc --- /dev/null +++ b/_module/nss/0i_talents.nss @@ -0,0 +1,3100 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0i_talents + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Fuctions to use a category of skills, feats, spells, or items. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_combat" +// ***************************************************************************** +// ************************* Try * Defensive Talents *************************** +// ***************************************************************************** +// These functions try to find and use a specific set of talents intelligently. + +// Returns TRUE if oCreature uses a healing talent on oTarge. +// nInMelee is the number of enemies the caller is in melee with. +// If oTarget is set then they will heal that target if they need it. +// Otherwise checks all allies to see who we should heal based on the talent. +int ai_TryHealingTalent(object oCreature, int nInMelee, object oTarget = OBJECT_INVALID); +// Returns TRUE if oCreature uses a cure condition talent on an ally or self. +int ai_TryCureConditionTalent(object oCreature, int nInMelee, object oTarget = OBJECT_INVALID); +// Returns TRUE if oCreature uses a defensive talent. +// Checks for a Defensive talent(Protection, Enhancement, or Summons). +// Randomizes the order to mix up spells in combat. +// if oTarget is set then the defensive talent will be cast on them or OBJECT_SELF. +int ai_TryDefensiveTalents(object oCreature, int nInMelee, int nMaxLevel, int nRound = 0, object oTarget = OBJECT_INVALID); +// Returns TRUE if oCreature uses a defensive talent. +// Checks the enemy faction for most powerful class and picks a buff based on it. +//int ai_TryAdvancedBuffOnSelf(object oCreature, int nInMelee); +// Set any auras this oCreature has instantly. +// This can be done in the OnSpawn script, heart beat, or Perception. +void ai_SetAura(object oCreature); + +// ***************************************************************************** +// ************************ Try Physical Attack Talents ************************ +// ***************************************************************************** +// These functions try to find and use melee attack talents intelligently. + +// Wrapper for ActionAttack, oCreature uses nAction (attack) on oTarget. +// nInMelee is only used in AI_LAST_ACTION_RANGED_ATK actions. +// bPassive TRUE oCreature will not move while attacking. +// nActionMode, pass the action mode if one is being used. +void ai_ActionAttack(object oCreature, int nAction, object oTarget, int nInMelee = 0, int bPassive = FALSE, int nActionMode = 0); +// Returns TRUE if oCreature uses a dragons breath talent +// Check for dragon's attacks under TALENT_CATEGORY_DRAGONS_BREATH(19). +// nRound must be supplied so we can keep track of the breath uses. +int ai_TryDragonBreathAttack(object oCreature, int nRound, object oTarget = OBJECT_INVALID); +// Returns TRUE if oCreature uses a dragons wing attacks. +// Checks to see if a dragon can use its wings on a nearby enemy. +// Checks the right side and then the left side to see if it can attack. +int ai_TryWingAttacks(object oCreature); +// Returns TRUE if oCreature uses a dragons tail slap. +// Looks behind the dragon to see if it can use it's tail slap on an enemy. +int ai_TryTailSlap(object oCreature); +// Returns TRUE if oCreature uses a dragons crush attack. +// Dragon can fly up and crash down on opponents to do bludgeoning damage. +// If 3 times smaller than the dragon they will take extra damage and be +// Knocked Down for 1 round if Reflex save is not made. +int ai_TryCrushAttack(object oCreature, object oTarget); +// Returns TRUE if oCreature uses a dragons tail sweep attack. +// If the enemy is 4 sizes smaller than it the dragon to use its tail to sweep +// behind it doing damage and knocking the opponents down. +int ai_TryTailSweepAttack(object oCreature); +// Returns TRUE if oCreature finds a good target and uses Sneak Attack. +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 melee talent. +int ai_TryMeleeTalents(object oCreature, object oTarget); +// ***************************************************************************** +// ******************************* Try * Skills ******************************** +// ***************************************************************************** +// These functions try to find and use a specific set of skills intelligently. + +// Wrapper to have oCreature use nSkill on oTarget. +void ai_UseSkill(object oCreature, int nSkill, object oTarget); +// Returns TRUE if oCreature uses the parry skill on someone attacking them. +// Checks if doing a parry might be successful. +int ai_TryParry(object oCreature); +// Returns TRUE if oCreature uses the Taunt skill on oTarget. +// Checks if doing a taunt might be successful against oTarget. +int ai_TryTaunt(object oCreature, object oTarget); +// Returns TRUE if oCreature uses the Animial emapthy skill on oTarget. +// For it to work oTarget must be an Animal, Beast, or Magical Beast. +// Checks if doing Animal Empathy might be successful against oTarget. +int ai_TryAnimalEmpathy(object oCreature, object oTarget = OBJECT_INVALID); +// ***************************************************************************** +// ******************************** Try * Feats ******************************** +// ***************************************************************************** +// These functions try to find and use a specific set of feats intelligently. + +// Wrapper to have oCreature use nFeat on oTarget. +void ai_UseFeat(object oCreature, int nFeat, object oTarget, int nSubFeat = 0); +// Wrapper to have oCreature use nActionMode on oTarget. +// nInMelee is only used in AI_LAST_ACTION_RANGED_ATK actions. +// bPassive TRUE oCreature will not move while attacking. +void ai_UseFeatAttackMode(object oCreature, int nActionMode, int nAction, object oTarget, int nInMelee = 0, int bPassive = FALSE); +// Returns TRUE if oCreature uses Rage. +// This checks if they are already in a rage and if they have the Rage feat. +int ai_TryBarbarianRageFeat(object oCreature); +// Returns TRUE if oCreature uses Bard song. +// This checks if they have any uses left, have the feat and if its viable. +int ai_TryBardSongFeat(object oCreature); +// Returns TRUE if oCreature uses Called shot. +// This checks if they have the feat and if its viable. +int ai_TryCalledShotFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Disarm. +// This checks if they have the feat and if its viable. +int ai_TryDisarmFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Divine Might. +// This only checks if they can use the feat and have turn undead uses left. +int ai_TryDivineMightFeat(object oCreature, int nInMelee); +// Returns TRUE if oCreature uses Divine Shield. +// This only checks if they can use the feat and have turn undead uses left. +int ai_TryDivineShieldFeat(object oCreature, int nInMelee); +// Returns TRUE if oCreature uses Expertise. +// This checks if they have the feat and if its viable. +// Also checks to see if the Improved Expertise feat would be better. +int ai_TryExpertiseFeat(object oCreature); +// Returns TRUE if oCreature uses Flurry of Blows. +// This checks if they have the feat and if its viable. +int ai_TryFlurryOfBlowsFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Improved Expertise. +// This checks if they have the feat and if its viable. +// Also checks to see if the Expertise feat would be better. +int ai_TryImprovedExpertiseFeat(object oCreature); +// Returns TRUE if oCreature uses Improved Power Attack. +// This checks if they have the feat and if its viable. +// Also checks to see if the Power Attack feat would be better. +int ai_TryImprovedPowerAttackFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Ki Damage. +// This checks if they have any uses left, have the feat and if its viable. +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 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); +// Returns TRUE if oCreature uses Power Attack. +// This checks if they have the feat and if its viable. +// Also checks to see if the Improved Power Attack would be better. +int ai_TryPowerAttackFeat(object oCreature, object oEnemy); +// Returns TRUE if oCreature uses Quivering palm. +// This checks if they have any uses left, have the feat and if its viable. +int ai_TryQuiveringPalmFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Power Attack. +// This checks if they have the feat and if its viable. +// Using a bow and having arrows should be checked before calling this. +int ai_TryRapidShotFeat(object oCreature, object oTarget, int nInMelee); +// Returns TRUE if oCreature uses Sap. +// This checks if they have the feat and if its viable. +int ai_TrySapFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Smite evil. +// This checks if they have any uses left, have the feat and if its viable. +int ai_TrySmiteEvilFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Smite good. +// This checks if they have any uses left, have the feat and if its viable. +int ai_TrySmiteGoodFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses Stunning fists. +// This checks if they have any uses left, have the feat and if its viable. +int ai_TryStunningFistFeat(object oCreature, object oTarget); +// Returns TRUE if oCreature uses a summon animal companion talent. +int ai_TrySummonAnimalCompanionTalent(object oCreature); +// Returns TRUE if oCreature uses a summon familiar talent. +int ai_TrySummonFamiliarTalent(object oCreature); +// Returns TRUE if oCreature uses the Lay on Hands feat talent. +int ai_TryLayOnHands(object oCreature); +// Returns TRUE if oCreature uses a turning talent. +int ai_TryTurningTalent(object oCreature); +// Returns TRUE if oCreature uses Whirlwind. +// This checks if they have the feat and if its viable. +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); +// ***************************************************************************** +// ***************************** 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. +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. +// Return a number equal to 1 and half the level of oCreature upto 10. +// The max spell level used is equal to nMaxLevel or less. +int ai_GetAssociateTalentMaxLevel(object oCreature, int nDifficulty); +// Returns TRUE if oCreature has nTalent. +// nTalent will be a spell in the spells.2da. +int ai_GetHasTalent(object oCreature, int nTalent); +// Saves a talent in JsonArray. +// Array: 0-Type (1-spell, 2-sp ability, 4-feat, 3-item) +// Type 1)spell 0-type, 1-spell, 2-class, 3-level, 4-slot. +// Type 2)sp Ability 0-type, 1-spell, 2-class, 3-level, 4-slot. +// Type 3)feat 0-type, 1-spell, 2- class, 3- level. +// 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); +// 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); +// 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. +// also Returns -1 if oCreature uses jTalent on oTarget with a memorized spell. +// This allows the user to remove jTalent from jLevel in jCategory. +int ai_UseCreatureSpellTalent(object oCreature, json jLevel, json jTalent, string sCategory, int nInMelee, object oTarget = OBJECT_INVALID); +// Return TRUE if oCreature uses a jTalent from oItem on oTarget. +int ai_UseCreatureItemTalent(object oCreature, json jLevel, json jTalent, string sCategory, int nInMelee, object oTarget = OBJECT_INVALID); +// Returns TRUE if oCreature uses a talent from sCategory of nLevel or less. +int ai_UseCreatureTalent(object oCreature, string sCategory, int nInMelee, int nLevel = 10, object oTarget = OBJECT_INVALID); +// Return TRUE if oCreature uses nTalent on oTarget. +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); +// 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); +// Return TRUE if oCreature uses jTalent on oTarget after checking special cases. +int ai_CheckSpecialTalentsandUse(object oCreature, json jTalent, string sCategory, int nInMelee, object oTarget); + +int ai_TryHealingTalent(object oCreature, int nInMelee, object oTarget = OBJECT_INVALID) +{ + // First lets evaluate oTarget and see how strong of a spell we will need. + if(oTarget != OBJECT_INVALID) + { + 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; + } + // We don't have a target so lets go check for one. + else + { + if(!ai_GetAIMode(oCreature, AI_MODE_PARTY_HEALING_OFF)) + { + // Lets not run past an enemy to heal unless we have the feats, bad tactics! + float fRange; + if(ai_CanIMoveInCombat(oCreature)) fRange = AI_RANGE_PERCEPTION; + else + { + fRange = GetDistanceBetween(oCreature, GetLocalObject(oCreature, AI_ENEMY_NEAREST)) - 3.0f; + // Looks bad when your right next to an ally, but technically the enemy is closer. + if(fRange < AI_RANGE_MELEE) fRange = AI_RANGE_MELEE; + } + oTarget = ai_GetAllyToHealTarget(oCreature, fRange); + } + else oTarget = oCreature; + if(oTarget == OBJECT_INVALID) return FALSE; + } + // Should we ignore associates? + if(ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && + 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) + + "< 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? + 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)) + { + // 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; + } + return FALSE; +} +int ai_CheckTargetVsConditions(object oTarget, json jTalent, int nConditions) +{ + // Check nCondition for any negative effects based on the talent we have. + switch(JsonGetInt(JsonArrayGet(jTalent, 1))) + { + case SPELL_NEUTRALIZE_POISON : + if(ai_GetHasNegativeCondition(AI_CONDITION_POISON, nConditions)) return TRUE; + break; + case SPELL_REMOVE_DISEASE : + if(ai_GetHasNegativeCondition(AI_CONDITION_DISEASE, nConditions)) return TRUE; + break; + case SPELL_REMOVE_BLINDNESS_AND_DEAFNESS : + if(ai_GetHasNegativeCondition(AI_CONDITION_BLINDDEAF, nConditions)) return TRUE; + break; + case SPELL_REMOVE_FEAR : + if(ai_GetHasNegativeCondition(AI_CONDITION_FRIGHTENED, nConditions)) return TRUE; + break; + case SPELL_REMOVE_CURSE : + if(ai_GetHasNegativeCondition(AI_CONDITION_CURSE, nConditions)) return TRUE; + break; + case SPELL_REMOVE_PARALYSIS : + if(ai_GetHasNegativeCondition(AI_CONDITION_PARALYZE, nConditions)) return TRUE; + break; + case SPELL_CLARITY : + if(ai_GetHasNegativeCondition(AI_CONDITION_DAZED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_CHARMED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_CONFUSED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_STUNNED, nConditions)) return TRUE; + break; + case SPELL_GREATER_RESTORATION : + if(ai_GetHasNegativeCondition(AI_CONDITION_DAZED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_CONFUSED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_DOMINATED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_SLOW, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_FRIGHTENED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_STUNNED, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_CHARMED, nConditions)) return TRUE; + case SPELL_RESTORATION : + if(ai_GetHasNegativeCondition(AI_CONDITION_LEVEL_DRAIN, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_BLINDDEAF, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_PARALYZE, nConditions)) return TRUE; + case SPELL_LESSER_RESTORATION : + if(ai_GetHasNegativeCondition(AI_CONDITION_ABILITY_DRAIN, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_SAVE_DECREASE, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_SR_DECREASE, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_SKILL_DECREASE, nConditions)) return TRUE; + if(ai_GetHasNegativeCondition(AI_CONDITION_AC_DECREASE , nConditions)) return TRUE; + 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; + } + return FALSE; +} +int ai_CheckTalentsVsConditions(object oCreature, int nConditions, int nInMelee, int nLevel, object oTarget) +{ + // 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(JsonGetType(jCategory) == JSON_TYPE_NULL) + { + SetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE, -1); + return FALSE; + } + // 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 + ": " + + IntToString(nMaxTalentLevel) + + " nLevel: " + IntToString(nLevel)); + if(nMaxTalentLevel < nLevel) nLevel = nMaxTalentLevel; + if(nLevel < 0 || nLevel > 10) nLevel = 9; + json jLevel, jTalent; + 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) + + " bUseMagicItems: " + IntToString(bUseMagicItems)); + // Loop through nLevels down to 0 looking for the first talent (i.e. the highest). + while(nLevel >= 0) + { + // 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) + + " 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", "391", "nSlotIndex: " + IntToString(nSlotIndex) + + " jTalent Type: " + IntToString(JsonGetType(jTalent))); + // Check to see if the talent matches oTargets nConditionss. + if(ai_CheckTargetVsConditions(oTarget, jTalent, nConditions)) + { + nType = JsonGetInt(JsonArrayGet(jTalent, 0)); + if(bUseMagic) + { + if(nType == AI_TALENT_TYPE_SPELL) + { + if(ai_CastInMelee(oCreature, JsonGetInt(JsonArrayGet(jTalent, 1)), 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; + } + } + else if(nType == AI_TALENT_TYPE_SP_ABILITY) + { + // Special ability spells do not need to concentrate?! + if(ai_CheckSpecialTalentsandUse(oCreature, jTalent, AI_TALENT_CURE, 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, AI_TALENT_CURE, nLevel, nSlotIndex); + return TRUE; + } + } + } + if(bUseMagicItems && nType == AI_TALENT_TYPE_ITEM) + { + // Items do not need to concentrate. + if(ai_UseCreatureItemTalent(oCreature, jLevel, jTalent, AI_TALENT_CURE, nInMelee, oTarget)) + { + if(AI_DEBUG) ai_Debug("0i_talents", "430", "Checking if Item is used up: " + + IntToString(JsonGetInt(JsonArrayGet(jTalent, 4)))); + if(JsonGetInt(JsonArrayGet(jTalent, 4)) == -1) + { + ai_RemoveTalent(oCreature, jCategory, jLevel, AI_TALENT_CURE, nLevel, nSlotIndex); + } + return TRUE; + } + } + } + nSlotIndex++; + } + } + else SetLocalInt(oCreature, AI_MAX_TALENT + AI_TALENT_CURE, nLevel - 1); + nLevel--; + } + return FALSE; +} +int ai_TryCureConditionTalent(object oCreature, int nInMelee, object oTarget = OBJECT_INVALID) +{ + // 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 + ": " + + 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; + object oTarget; + if(oTarget == OBJECT_INVALID) + { + oTarget = GetLocalObject(oCreature, AI_ALLY + "1"); + while(oTarget != OBJECT_INVALID) + { + nTargetNegConds = ai_GetNegativeConditions(oTarget); + // Should we ignore associates? + if(!ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) || + GetAssociateType(oTarget) < 2) + { + if(nNegativeConditions < nTargetNegConds) + { + nNegativeConditions = nTargetNegConds; + nIndex = nCnt; + } + } + oTarget = GetLocalObject(oCreature, AI_ALLY + IntToString(++nCnt)); + } + // No one has a negative condition then get out. + if(!nNegativeConditions) return FALSE; + oTarget = GetLocalObject(oCreature, AI_ALLY + IntToString(nIndex)); + } + else + { + nNegativeConditions = ai_GetNegativeConditions(oTarget); + if(!nNegativeConditions) return FALSE; + } + 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) + + " on " + GetName(oTarget)); + if(ai_CheckTalentsVsConditions(oCreature, nNegativeConditions, nInMelee, 9, oTarget)) return TRUE; + return FALSE; +} +// ***************************************************************************** +// ************************* Try * Defensive Talents *************************** +// ***************************************************************************** +// These functions try to find and use a specific set of talents intelligently. + +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. + 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; + // Try to mix them up so we don't always cast spells in the same order. + int nRoll = d2(); + if(AI_DEBUG) ai_Debug("0i_talents", "507", "Lets help someone(Check Talents: " +IntToString(nRoll) + + " nMaxLevel: " + IntToString(nMaxLevel) + ")!"); + if(nRoll == 1) + { + if(ai_UseCreatureTalent(oCreature, AI_TALENT_ENHANCEMENT, nInMelee, nMaxLevel, oTarget)) return TRUE; + if(ai_UseCreatureTalent(oCreature, AI_TALENT_PROTECTION, nInMelee, nMaxLevel, oTarget)) return TRUE; + } + else if(nRoll == 2) + { + if(ai_UseCreatureTalent(oCreature, AI_TALENT_PROTECTION, nInMelee, nMaxLevel, oTarget)) return TRUE; + if(ai_UseCreatureTalent(oCreature, AI_TALENT_ENHANCEMENT, nInMelee, nMaxLevel, oTarget)) return TRUE; + } + return FALSE; +} +void ai_SetAura(object oCreature) +{ + // Cycle through a creatures special abilities and use any auras. + int bCanUse, nIndex = 0, nMaxSpAbility = GetSpellAbilityCount(oCreature); + int nSpell = GetSpellAbilitySpell(oCreature, nIndex); + while(nIndex < nMaxSpAbility) + { + bCanUse = FALSE; + if(GetSpellAbilityReady(oCreature, nIndex)) + { + if(nSpell == SPELLABILITY_AURA_BLINDING) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_COLD) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_ELECTRICITY) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_FEAR) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_FIRE) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_HORRIFICAPPEARANCE) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_MENACE) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_HORRIFICAPPEARANCE) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_PROTECTION) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_STUN) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_UNEARTHLY_VISAGE) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_UNNATURAL) bCanUse = TRUE; + else if(nSpell == SPELLABILITY_AURA_HORRIFICAPPEARANCE) bCanUse = TRUE; + else if(nSpell == 306 /*SPELLABILITY_AURA_TYRANT_FOG_MIST*/) bCanUse = TRUE; + else if(nSpell == 412 /*SPELLABILITY_AURA_DRAGON_FEAR*/) bCanUse = TRUE; + else if(nSpell == 761 /*SPELLABILITY_AURA_HELLFIRE*/) bCanUse = TRUE; + else if(nSpell == 805/*SPELLABILITY_AURA_TROGLODYTE_STENCH*/) bCanUse = TRUE; + } + if(bCanUse) ActionCastSpellAtObject(nSpell, oCreature, 255, FALSE, 0, 0, TRUE); + nSpell = GetSpellAbilitySpell(oCreature, ++nIndex); + } +} +// ***************************************************************************** +// ************************* Try * Skills ************************************** +// ***************************************************************************** +// These functions try to find and use a specific set of skills intelligently. + +void ai_UseSkill(object oCreature, int nSkill, object oTarget) +{ + ai_SetLastAction(oCreature, AI_LAST_ACTION_USED_SKILL); + if(GetIsEnemy(oTarget, oCreature)) SetLocalObject(oCreature, AI_ATTACKED_PHYSICAL, oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "498", GetName(oCreature) + " is using skill: " + + GetStringByStrRef(StringToInt(Get2DAString("skills", "Name", nSkill))) + + " on " + GetName(oTarget)); + ActionUseSkill(nSkill, oTarget); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); +} +int ai_TryParry(object oCreature) +{ + // Only use parry on an active melee attacker + object oTarget = GetLastHostileActor(oCreature); + // If we are already in parry mode then lets keep it up. + if(GetActionMode(oCreature, ACTION_MODE_PARRY) && + GetCurrentAction(oCreature) == ACTION_ATTACKOBJECT) return TRUE; + if(oTarget == OBJECT_INVALID || + ai_GetAttackedTarget(oTarget) != oCreature || + !ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget))) return FALSE; + // Only if our parry skill > their attack bonus + 5 + d10 + // Parry has a -4 atk adjustment. Our chance to hit should be 75% + d10. + // EnemyAtk(20) - OurParrySkill(10) = 0 + d10(75% to 25% chance to hit). + int nParrySkill = GetSkillRank(SKILL_PARRY, oCreature); + int nAtk = ai_GetCreatureAttackBonus(oTarget); + if(nAtk - nParrySkill >= 0 + d10()) return FALSE; + ai_EquipBestMeleeWeapon(oCreature, oTarget); + SetActionMode(oCreature, ACTION_MODE_PARRY, TRUE); + ai_SetLastAction(oCreature, AI_LAST_ACTION_USED_SKILL); + ActionAttack(oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "524", "Using parry against " + GetName(oTarget) + "!"); + return TRUE; +} +int ai_TryTaunt(object oCreature, object oTarget) +{ + int nCoolDown = GetLocalInt(oCreature, "AI_TAUNT_COOLDOWN"); + if(AI_DEBUG) ai_Debug("0i_talents", "530", "Has Taunt Effect? " + + IntToString(ai_GetHasEffectType(oTarget, EFFECT_TYPE_TAUNT)) + + " Cooldown: " + IntToString(nCoolDown)); + if(nCoolDown > 0) + { + SetLocalInt(oCreature, "AI_TAUNT_COOLDOWN", --nCoolDown); + return FALSE; + } + if(!ai_GetHasEffectType(oTarget, EFFECT_TYPE_TAUNT)) return FALSE; + // Check to see if we have a good chance for it to work. + int nTauntRnk = GetSkillRank(SKILL_TAUNT, oCreature); + if(AI_DEBUG) ai_Debug("0i_talents", "542", "Check Taunt: TauntRnk: " + IntToString(nTauntRnk) + + " HitDice + 1: " + IntToString(GetHitDice(oCreature) + 1) + + " Concentration: " + IntToString(GetSkillRank(SKILL_CONCENTRATION, oTarget)) + "."); + int nConcentration = GetSkillRank(SKILL_CONCENTRATION, oTarget); + // Our chance is greater than 50%. + if(nTauntRnk <= nConcentration) return FALSE; + ai_UseSkill(oCreature, SKILL_TAUNT, oTarget); + SetLocalInt(oCreature, "AI_TAUNT_COOLDOWN", AI_TAUNT_COOLDOWN); + return TRUE; +} +int ai_TryAnimalEmpathy(object oCreature, object oTarget = OBJECT_INVALID) +{ + if(!GetSkillRank(SKILL_ANIMAL_EMPATHY, oCreature)) return FALSE; + int nCoolDown = GetLocalInt(oCreature, "AI_EMPATHY_COOLDOWN"); + if(AI_DEBUG) ai_Debug("0i_talents", "556", "Has Dominate Effect? " + + IntToString(ai_GetHasEffectType(oTarget, EFFECT_TYPE_DOMINATED)) + + " Cooldown: " + IntToString(nCoolDown)); + if(nCoolDown > 0) + { + SetLocalInt(oCreature, "AI_EMPATHY_COOLDOWN", --nCoolDown); + return FALSE; + } + if(oTarget == OBJECT_INVALID) + { + oTarget = ai_GetNearestRacialTarget(oCreature, AI_RACIAL_TYPE_ANIMAL_BEAST); + if(oTarget == OBJECT_INVALID) return FALSE; + } + if(!GetObjectSeen(oCreature, oTarget)) return FALSE; + if(ai_GetHasEffectType(oTarget, EFFECT_TYPE_DOMINATED) || + GetIsImmune(oTarget, IMMUNITY_TYPE_MIND_SPELLS) || + GetIsImmune(oTarget, IMMUNITY_TYPE_DOMINATE) || + GetAssociateType(oTarget) != ASSOCIATE_TYPE_NONE) return FALSE; + // Get the race of the target, it only works on Animals, Beasts, and Magical Beasts. + int nRace = GetRacialType(oTarget); + int nDC; + if(nRace == RACIAL_TYPE_ANIMAL) nDC = 5; + else if(nRace == RACIAL_TYPE_BEAST || nRace == RACIAL_TYPE_MAGICAL_BEAST) nDC = 9; + else return FALSE; + // Check to see if we have a good chance for it to work. + int nEmpathyRnk = GetSkillRank(SKILL_ANIMAL_EMPATHY, oCreature); + nDC += GetHitDice(oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "632", "Check Animal Empathy: Rnk: " + IntToString(nEmpathyRnk) + + " nDC: " + IntToString(nDC) + "."); + // Our chance is greater than 50%. + if(nEmpathyRnk <= nDC) return FALSE; + ai_UseSkill(oCreature, SKILL_ANIMAL_EMPATHY, oTarget); + SetLocalInt(oCreature, "AI_EMPATHY_COOLDOWN", AI_EMPATHY_COOLDOWN); + return TRUE; +} +// ***************************************************************************** +// ************************* Try * Feats *************************************** +// ***************************************************************************** +// These functions try to find and use a specific set of feats intelligently. + +void ai_UseFeat(object oCreature, int nFeat, object oTarget, int nSubFeat = 0) +{ + ai_SetLastAction(oCreature, AI_LAST_ACTION_USED_FEAT); + if(GetIsEnemy(oTarget, oCreature)) SetLocalObject(oCreature, AI_ATTACKED_PHYSICAL, oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "600", GetName(oCreature) + " is using feat: " + + GetStringByStrRef(StringToInt(Get2DAString("feat", "FEAT", nFeat))) + + " on " + GetName(oTarget)); + ActionUseFeat(nFeat, oTarget, nSubFeat); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); +} +void ai_UseFeatAttackMode(object oCreature, int nActionMode, int nAction, object oTarget, int nInMelee = 0, int bPassive = FALSE) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "608", "Action mode (" + IntToString(nActionMode) + ") Is it set?: " + + IntToString(GetActionMode(oCreature, nActionMode))); + if(!GetActionMode(oCreature, nActionMode)) + { + if(AI_DEBUG) ai_Debug("0i_talents", "612", "Setting action mode: " + IntToString(nActionMode)); + SetActionMode(oCreature, nActionMode, TRUE); + SetLocalInt(oCreature, AI_CURRENT_ACTION_MODE, nActionMode); + } + ai_ActionAttack(oCreature, nAction, oTarget, nInMelee, bPassive, nActionMode); +} +int ai_TryBarbarianRageFeat(object oCreature) +{ + // Must not have rage already, must have the feat, and enemy must be strong enough. + if(GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) || + !GetHasFeat(FEAT_BARBARIAN_RAGE, oCreature)) return FALSE; + ai_UseFeat(oCreature, FEAT_BARBARIAN_RAGE, oCreature); + return TRUE; +} +int ai_TryBardSongFeat(object oCreature) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "629", "BardSong Effect: " + IntToString(GetHasSpellEffect(411/*SPELL_BARD_SONG*/)) + + " Level: " + IntToString(GetLevelByClass(CLASS_TYPE_BARD)) + + " HasFeat: " + IntToString(GetHasFeat(FEAT_BARD_SONGS))); + if(GetHasSpellEffect(411/*SPELL_BARD_SONG*/, oCreature) || + !GetHasFeat(FEAT_BARD_SONGS, oCreature)) return FALSE; + ai_UseFeat(oCreature, FEAT_BARD_SONGS, oCreature); + return TRUE; +} +int ai_TryCalledShotFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_CALLED_SHOT, oCreature)) return FALSE; + // Called shot has a -4 to hit adjustment. + if(!ai_AttackPenaltyOk(oCreature, oTarget, -4.0)) return FALSE; + ai_UseFeat(oCreature, FEAT_CALLED_SHOT, oTarget); + return TRUE; +} +int ai_TryDisarmFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_DISARM, oCreature)) return FALSE; + // If we can't disarm them then get out! + if(!GetIsCreatureDisarmable(oTarget)) return FALSE; + int nEAC = GetAC(oTarget); + int nOAtk = ai_GetCreatureAttackBonus(oCreature); + // The combatant with the larger weapon gains +4 per size category. + // Weapon Size in the baseitems.2da is 1 = Tiny, 2 = Small, 3 = Medium, 4 = Large. + int nOWeaponType = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); + int nOWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nOWeaponType)); + int nEWeaponType = GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget)); + int nEWeaponSize = StringToInt(Get2DAString("baseitems", "WeaponSize", nEWeaponType)); + nOAtk +=(nOWeaponSize - nEWeaponSize) * 4; + // Do they have Improved Disarm? + if(GetHasFeat(FEAT_IMPROVED_DISARM, oCreature)) nOAtk += 2; + // Disarm has a -6 atk adjustment. + if(!ai_AttackPenaltyOk(oCreature, oTarget, -6.0)) return FALSE; + ai_UseFeat(oCreature, FEAT_DISARM, oTarget); + return TRUE; +} +int ai_TryDivineMightFeat(object oCreature, int nInMelee) +{ + if(!GetHasFeat(FEAT_TURN_UNDEAD)) return FALSE; + if(!GetHasFeat(FEAT_DIVINE_MIGHT)) return FALSE; + if(GetHasFeatEffect(FEAT_DIVINE_MIGHT, oCreature)) return FALSE; + if(!nInMelee) return FALSE; + object oTarget = ai_GetEnemyAttackingMe(oCreature); + if(oTarget == OBJECT_INVALID) return FALSE; + float fAtkAdj = IntToFloat(GetAbilityModifier(ABILITY_CHARISMA, oCreature)); + if(!ai_AttackBonusGood(oCreature, oTarget, fAtkAdj)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "722", "USING DIVINE MIGHT on " + GetName(oCreature) + "."); + ai_UseFeat(oCreature, FEAT_DIVINE_MIGHT, oCreature); + return TRUE; +} +int ai_TryDivineShieldFeat(object oCreature, int nInMelee) +{ + if(!GetHasFeat(FEAT_TURN_UNDEAD)) return FALSE; + if(!GetHasFeat(FEAT_DIVINE_SHIELD)) return FALSE; + if(GetHasFeatEffect(FEAT_DIVINE_SHIELD, oCreature)) return FALSE; + if(!nInMelee) return FALSE; + object oTarget = ai_GetEnemyAttackingMe(oCreature); + if(oTarget == OBJECT_INVALID) return FALSE; + float fACAdj = IntToFloat(GetAbilityModifier(ABILITY_CHARISMA, oCreature)); + if(!ai_ACAdjustmentGood(oCreature, oTarget, fACAdj)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "736", "USING DIVINE SHIELD on " + GetName(oCreature) + "."); + ai_UseFeat(oCreature, FEAT_DIVINE_SHIELD, oCreature); + return TRUE; +} +int ai_TryExpertiseFeat(object oCreature) +{ + if(!GetHasFeat(FEAT_EXPERTISE, oCreature)) return FALSE; + object oTarget = ai_GetEnemyAttackingMe(oCreature); + // Expertise has a -5 atk and a +5 AC adjustment. + if(oTarget == OBJECT_INVALID || + !ai_AttackPenaltyOk(oCreature, oTarget, -5.0) || + !ai_ACAdjustmentGood(oCreature, oTarget, 5.0)) + { + SetActionMode(oCreature, ACTION_MODE_EXPERTISE, FALSE); + DeleteLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_talents", "704", "USING EXPERTISE on " + GetName(oTarget) + "."); + ai_UseFeatAttackMode(oCreature, ACTION_MODE_EXPERTISE, AI_LAST_ACTION_MELEE_ATK, oTarget); + return TRUE; +} +int ai_TryFlurryOfBlowsFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_FLURRY_OF_BLOWS, oCreature)) return FALSE; + // Flurry of Blows has a -2 atk adjustment. + if(!ai_AttackPenaltyOk(oCreature, oTarget, -2.0)) + { + SetActionMode(oCreature, ACTION_MODE_FLURRY_OF_BLOWS, FALSE); + DeleteLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_talents", "718", "USING FLURRY OF BLOWS on " + GetName(oTarget) + "."); + ai_UseFeatAttackMode(oCreature, ACTION_MODE_FLURRY_OF_BLOWS, AI_LAST_ACTION_MELEE_ATK, oTarget, TRUE); + return TRUE; +} +int ai_TryImprovedExpertiseFeat(object oCreature) +{ + if(!GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature)) return FALSE; + object oTarget = ai_GetEnemyAttackingMe(oCreature); + // Improved expertise has a -10 atk +10 AC adjustment. + if(oTarget == OBJECT_INVALID || + !ai_AttackPenaltyOk(oCreature, oTarget, -10.0) || + !ai_ACAdjustmentGood(oCreature, oTarget, 10.0)) + { + SetActionMode(oCreature, ACTION_MODE_IMPROVED_EXPERTISE, FALSE); + DeleteLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_talents", "735", "USING IMPROVED EXPERTISE on " + GetName(oTarget) + "."); + ai_UseFeatAttackMode(oCreature, ACTION_MODE_IMPROVED_EXPERTISE, AI_LAST_ACTION_MELEE_ATK, oTarget); + return TRUE; +} +int ai_TryImprovedPowerAttackFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_IMPROVED_POWER_ATTACK, oCreature)) return FALSE; + // Improved Power Attack has a -10 atk adjustment. + // If we cannot hit or will kill in one hit then maybe we should use Power Attack instead. + if(ai_PowerAttackGood(oCreature, oTarget, 10.0)) + { + ai_UseFeatAttackMode(oCreature, ACTION_MODE_IMPROVED_POWER_ATTACK, AI_LAST_ACTION_MELEE_ATK, oTarget); + return TRUE; + } + SetActionMode(oCreature, ACTION_MODE_IMPROVED_POWER_ATTACK, FALSE); + DeleteLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + return ai_TryPowerAttackFeat(oCreature, oTarget); +} +int ai_TryKiDamageFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_KI_DAMAGE, oCreature)) return FALSE; + // Must have > 40 hitpoints AND + // Damage reduction OR damage resistance + // or just have over 200 hitpoints + int bHasDamageReduction = FALSE; + int bHasDamageResistance = FALSE; + int bHasHitpoints = FALSE; + int bHasMassiveHitpoints = FALSE; + int bOutNumbered; + int nCurrentHP = GetCurrentHitPoints(oTarget); + if(nCurrentHP > 40) bHasHitpoints = TRUE; + if(nCurrentHP > 200) bHasMassiveHitpoints = TRUE; + if(ai_GetHasEffectType(oTarget, EFFECT_TYPE_DAMAGE_REDUCTION)) bHasDamageReduction = TRUE; + if(ai_GetHasEffectType(oTarget, EFFECT_TYPE_DAMAGE_RESISTANCE)) bHasDamageResistance = TRUE; + if(ai_GetNearestEnemy(oCreature, 3, 7, 7) != OBJECT_INVALID) bOutNumbered = TRUE; + if((!bHasHitpoints || (!bHasDamageReduction && !bHasDamageResistance)) && + (!bHasMassiveHitpoints) && (!bHasHitpoints || !bOutNumbered)) return FALSE; + ai_UseFeat(oCreature, FEAT_KI_DAMAGE, oTarget); + return TRUE; +} +int ai_TryKnockdownFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_KNOCKDOWN, oCreature)) return FALSE; + int nMySize = GetCreatureSize(oCreature); + if(GetHasFeat(FEAT_IMPROVED_KNOCKDOWN, oCreature)) nMySize++; + // Prevent silly use of knockdown on immune or too-large targets. + // Knockdown has a -4 atk adjustment. + if(GetIsImmune(oTarget, IMMUNITY_TYPE_KNOCKDOWN) || + GetCreatureSize(oTarget) > nMySize + 1 || + !ai_AttackPenaltyOk(oCreature, oTarget, -4.0)) return FALSE; + ai_UseFeat(oCreature, FEAT_KNOCKDOWN, oTarget); + return TRUE; +} +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; + } + 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; + } + 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; + } + 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; + } + else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_4)) + { + int nSubFeat; + int nRoll = d3(); + 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; + } + else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_3)) + { + int nSubFeat; + int nRoll = d3(); + 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; + } + else if(GetHasFeat(FEAT_GREATER_WILDSHAPE_2)) + { + int nSubFeat; + int nRoll = d3(); + 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; + } + 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(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; + } + 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; + } + else if(GetHasFeat(FEAT_WILD_SHAPE)) + { + int nSubFeat; + int nCompanionType = GetAnimalCompanionCreatureType(oCreature); + if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_NONE) + nSubFeat = Random(5) + SUBFEAT_WILD_SHAPE_BROWN_BEAR; + else + { + if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_BADGER) + nSubFeat = SUBFEAT_WILD_SHAPE_BADGER; + else if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_BOAR) + nSubFeat = SUBFEAT_WILD_SHAPE_BOAR; + else if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_BEAR) + nSubFeat = SUBFEAT_WILD_SHAPE_BROWN_BEAR; + else if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_PANTHER) + nSubFeat = SUBFEAT_WILD_SHAPE_PANTHER; + else if(nCompanionType == ANIMAL_COMPANION_CREATURE_TYPE_WOLF) + 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)); + ai_UseFeat(oCreature, FEAT_WILD_SHAPE, oCreature, nSubFeat); + return TRUE; + } + return FALSE; +} +int ai_TryPowerAttackFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_POWER_ATTACK, oCreature)) return FALSE; + // Power Attack has a -5 atk adjustment. + if(ai_PowerAttackGood(oCreature, oTarget, 5.0)) + { + ai_UseFeatAttackMode(oCreature, ACTION_MODE_POWER_ATTACK, AI_LAST_ACTION_MELEE_ATK, oTarget); + return TRUE; + } + SetActionMode(oCreature, ACTION_MODE_POWER_ATTACK, FALSE); + DeleteLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + return FALSE; +} +int ai_TryQuiveringPalmFeat(object oCreature, object oTarget) +{ + // Must have the feat, and enemy must be lower level, and not immune to crits. + if(!GetHasFeat(FEAT_QUIVERING_PALM, oCreature) || + GetHitDice(oTarget) >= GetHitDice(oCreature) || + GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)) return FALSE; + ai_UseFeat(oCreature, FEAT_QUIVERING_PALM, oTarget); + return TRUE; +} +int ai_TryRapidShotFeat(object oCreature, object oTarget, int nInMelee) +{ + if(!GetHasFeat(FEAT_RAPID_SHOT, oCreature)) return FALSE; + // Rapidshot has a -4 atk adjustment. + if(!ai_AttackPenaltyOk(oCreature, oTarget, -4.0)) + { + SetActionMode(oCreature, ACTION_MODE_RAPID_SHOT, FALSE); + DeleteLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + return FALSE; + } + ai_UseFeatAttackMode(oCreature, ACTION_MODE_RAPID_SHOT, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return TRUE; +} +int ai_TrySapFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_SAP, oCreature)) return FALSE; + // Does not work on creatures that cannot be hit by criticals or stunned. + // Sap has a -4 atk adjustment. + if(GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT) || + GetIsImmune(oTarget, IMMUNITY_TYPE_STUN) || + !ai_AttackPenaltyOk(oCreature, oTarget, -4.0)) return FALSE; + ai_UseFeat(oCreature, FEAT_SAP, oTarget); + return TRUE; +} +int ai_TrySmiteEvilFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_SMITE_EVIL, oCreature) || + GetAlignmentGoodEvil(oTarget) != ALIGNMENT_EVIL || + !ai_StrongOpponent(oCreature, oTarget)) return FALSE; + ai_UseFeat(oCreature, FEAT_SMITE_EVIL, oTarget); + return TRUE; +} +int ai_TrySmiteGoodFeat(object oCreature, object oTarget) +{ + if(!GetHasFeat(FEAT_SMITE_GOOD, oCreature) || + GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD || + !ai_StrongOpponent(oCreature, oTarget)) return FALSE; + ai_UseFeat(oCreature, FEAT_SMITE_GOOD, oTarget); + return TRUE; +} +int ai_TryStunningFistFeat(object oCreature, object oTarget) +{ + // Cannot use if we have a weapon equiped. + if(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature) != OBJECT_INVALID) return FALSE; + // Does not work on creatures that cannot be hit by criticals or stunned. + // Stunning Fists has a -4 atk adjustment. + if(!GetHasFeat(FEAT_STUNNING_FIST, oCreature) || + GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT) || + GetIsImmune(oTarget, IMMUNITY_TYPE_STUN) || + !ai_StrongOpponent(oCreature, oTarget) || + !ai_AttackPenaltyOk(oCreature, oTarget, -4.0)) return FALSE; + ai_UseFeat(oCreature, FEAT_STUNNING_FIST, oTarget); + return TRUE; +} +void ai_NameAssociate(object oCreature, int nAssociateType, string sName) +{ + object oAssociate = GetAssociate(nAssociateType, oCreature); + if(GetName(oCreature) != "") return; + SetName(oAssociate, sName); + ChangeFaction(oAssociate, oCreature); +} +int ai_TrySummonAnimalCompanionTalent(object oCreature) +{ + if(!GetHasFeat(FEAT_ANIMAL_COMPANION, oCreature)) return FALSE; + if(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCreature) != OBJECT_INVALID) return FALSE; + ai_UseFeat(oCreature, FEAT_ANIMAL_COMPANION, oCreature); + DelayCommand(0.0, ai_NameAssociate(oCreature, ASSOCIATE_TYPE_FAMILIAR, "Animal Companion")); + return TRUE; +} +int ai_TrySummonFamiliarTalent(object oCreature) +{ + if(!GetHasFeat(FEAT_SUMMON_FAMILIAR, oCreature)) return FALSE; + if(GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oCreature) != OBJECT_INVALID) return FALSE; + ai_UseFeat(oCreature, FEAT_SUMMON_FAMILIAR, oCreature); + DelayCommand(0.0, ai_NameAssociate(oCreature, ASSOCIATE_TYPE_FAMILIAR, "Familiar")); + return TRUE; +} +int ai_TryLayOnHands(object oCreature) +{ + if(!GetHasFeat(FEAT_LAY_ON_HANDS, oCreature)) return FALSE; + // Lets not run past an enemy to use touch atk unless we have the feats, bad tactics! + float fRange; + if(ai_CanIMoveInCombat(oCreature)) fRange = AI_RANGE_PERCEPTION; + else + { + fRange = GetDistanceBetween(oCreature, GetLocalObject(oCreature, AI_ENEMY_NEAREST)) - 3.0f; + // Looks bad when your right next to an ally, but technically the enemy is closer. + if(fRange < AI_RANGE_MELEE) fRange = AI_RANGE_MELEE; + } + object oTarget = ai_GetLowestCRRacialTarget(oCreature, RACIAL_TYPE_UNDEAD, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; + ai_UseFeat(oCreature, FEAT_LAY_ON_HANDS, oTarget); + return TRUE; +} +int ai_TryTurningTalent(object oCreature) +{ + if(!GetHasFeat(FEAT_TURN_UNDEAD, oCreature)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1043", "Checking for Turning Targets."); + int nHDCount, nHDCount2, nRacial, nHD; + // Get characters levels. + int nClericLevel = GetLevelByClass(CLASS_TYPE_CLERIC, oCreature); + int nPaladinLevel = GetLevelByClass(CLASS_TYPE_PALADIN, oCreature); + int nBlackguardlevel = GetLevelByClass(CLASS_TYPE_BLACKGUARD, oCreature); + int nTotalLevel = GetHitDice(oCreature); + int nTurnLevel = nClericLevel; + int nClassLevel = nClericLevel; + // GZ: Since paladin levels stack when turning, blackguard levels should stack as well + // GZ: but not with the paladin levels (thus else if). + if(nBlackguardlevel - 2 > 0 && nBlackguardlevel > nPaladinLevel) + { + nClassLevel += (nBlackguardlevel - 2); + nTurnLevel += (nBlackguardlevel - 2); + } + else if(nPaladinLevel - 2 > 0) + { + nClassLevel += (nPaladinLevel - 2); + nTurnLevel += (nPaladinLevel - 2); + } + //Flags for bonus turning types + int nElemental = GetHasFeat(FEAT_AIR_DOMAIN_POWER, oCreature) + + GetHasFeat(FEAT_EARTH_DOMAIN_POWER, oCreature) + + GetHasFeat(FEAT_FIRE_DOMAIN_POWER, oCreature) + + GetHasFeat(FEAT_WATER_DOMAIN_POWER, oCreature); + int nVermin = GetHasFeat(FEAT_PLANT_DOMAIN_POWER, oCreature); + int nConstructs = GetHasFeat(FEAT_DESTRUCTION_DOMAIN_POWER, oCreature); + int nGoodOrEvilDomain = GetHasFeat(FEAT_GOOD_DOMAIN_POWER, oCreature) + + GetHasFeat(FEAT_EVIL_DOMAIN_POWER, oCreature); + int nPlanar = GetHasFeat(854, oCreature); + // Get turning check average, modify if have the Sun Domain + int nChrMod = GetAbilityModifier(ABILITY_CHARISMA, oCreature); + int nTurnCheck = 15 + nChrMod; //The roll to apply to the max HD of undead that can be turned --> nTurnLevel + int nTurnHD = 12 + nChrMod + nClassLevel; //The number of HD of undead that can be turned. + if(GetHasFeat(FEAT_SUN_DOMAIN_POWER, oCreature)) + { + nTurnCheck += 2; + nTurnHD += 3; + } + //Determine the maximum HD of the undead that can be turned using a roll of 15 + ChrMod. + if(nTurnCheck == 15) nTurnLevel += 1; + else if(nTurnCheck >= 16 && nTurnCheck <= 18) nTurnLevel += 2; + else if(nTurnCheck >= 19 && nTurnCheck <= 21) nTurnLevel += 3; + else if(nTurnCheck >= 22) nTurnLevel += 4; + // Collect the number of HitDice we will affect. + int nCnt = 1; + object oEnemy = GetNearestCreature(7, 7, oCreature, nCnt); + while(oEnemy != OBJECT_INVALID && nHDCount < nTurnHD && GetDistanceBetween(oEnemy, oCreature) <= 20.0) + { + if(GetIsEnemy(oEnemy, oCreature) && !ai_Disabled(oEnemy)) + { + nRacial = GetRacialType(oEnemy); + nHD = 0; + if(nRacial == RACIAL_TYPE_UNDEAD) nHD = GetHitDice(oEnemy) + GetTurnResistanceHD(oEnemy); + else if(nRacial == RACIAL_TYPE_OUTSIDER && nGoodOrEvilDomain + nPlanar > 0) + { + //Planar turning decreases spell resistance against turning by 1/2 + if(nPlanar) nHD = GetHitDice(oEnemy) + (GetSpellResistance(oEnemy) / 2); + else nHD = GetHitDice(oEnemy) + GetSpellResistance(oEnemy); + } + else if(nRacial == RACIAL_TYPE_VERMIN && nVermin > 0) nHD = GetHitDice(oEnemy); + else if(nRacial == RACIAL_TYPE_ELEMENTAL && nElemental > 0) nHD = GetHitDice(oEnemy); + else if (nRacial == RACIAL_TYPE_CONSTRUCT && nConstructs > 0) nHD = GetHitDice(oEnemy); + // Only count undead we can defeat! + if(AI_DEBUG) ai_Debug("0i_talents", "1110", " nHD: " + IntToString(nHD) + + " nTurnLevel: " + IntToString(nTurnLevel) + + " nTurnHD: " + IntToString(nTurnHD) + + " nHDCount: " + IntToString(nHDCount)); + if(nHD > 0 && nHD <= nTurnLevel && nHD <= (nTurnHD - nHDCount)) nHDCount += nHD; + } + oEnemy = GetNearestCreature(7, 7, oCreature, ++nCnt); + } + if(AI_DEBUG) ai_Debug("0i_talents", "1089", "Found " + IntToString(nHDCount) + " hitdice to turn from my location."); + // Lets do one more check to see if we can get a better position to use TurnUndead. + nCnt = 1; + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + if(GetDistanceBetween(oCreature, oNearestEnemy) > AI_RANGE_MELEE) + { + oEnemy = oNearestEnemy; + if(AI_DEBUG) ai_Debug("0i_talents", "1126", GetName(oEnemy)); + while(oEnemy != OBJECT_INVALID && nHDCount2 < nTurnHD && GetDistanceBetween(oEnemy, oNearestEnemy) <= 20.0) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1129", GetName(oEnemy)); + if(GetIsEnemy(oEnemy, oCreature) && !ai_Disabled(oEnemy)) + { + nRacial = GetRacialType(oEnemy); + nHD = 0; + if(nRacial == RACIAL_TYPE_UNDEAD) nHD = GetHitDice(oEnemy) + GetTurnResistanceHD(oEnemy); + else if(nRacial == RACIAL_TYPE_OUTSIDER && nGoodOrEvilDomain + nPlanar > 0) + { + //Planar turning decreases spell resistance against turning by 1/2 + if(nPlanar) nHD = GetHitDice(oEnemy) + (GetSpellResistance(oEnemy) / 2); + else nHD = GetHitDice(oEnemy) + GetSpellResistance(oEnemy); + } + else if(nRacial == RACIAL_TYPE_VERMIN && nVermin > 0) nHD = GetHitDice(oEnemy); + else if(nRacial == RACIAL_TYPE_ELEMENTAL && nElemental > 0) nHD = GetHitDice(oEnemy); + else if (nRacial == RACIAL_TYPE_CONSTRUCT && nConstructs > 0) nHD = GetHitDice(oEnemy); + // Only count undead we can defeat! + if(AI_DEBUG) ai_Debug("0i_talents", "1140", " nHD: " + IntToString(nHD) + + " nTurnLevel: " + IntToString(nTurnLevel) + + " nTurnHD: " + IntToString(nTurnHD) + + " nHDCount2: " + IntToString(nHDCount2)); + if(nHD > 0 && nHD <= nTurnLevel && nHD <= (nTurnHD - nHDCount2)) nHDCount2 += nHD; + } + oEnemy = GetNearestCreature(7, 7, oNearestEnemy, ++nCnt); + } + } + if(AI_DEBUG) ai_Debug("0i_talents", "1148", "Found " + IntToString(nHDCount2) + " hitdice to turn from enemy location."); + if(nHDCount > nHDCount2) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1176", " My Location - nHDCount: " + IntToString(nHDCount) + + " >= nTurnHD / 2: " + IntToString(nTurnHD / 2)); + if(nHDCount < nTurnHD / 2) return FALSE; + ai_UseFeat(oCreature, FEAT_TURN_UNDEAD, oCreature); + return TRUE; + } + else + { + if(AI_DEBUG) ai_Debug("0i_talents", "1184", " Better location - nHDCount2: " + IntToString(nHDCount2) + + " >= nTurnHD / 2: " + IntToString(nTurnHD / 2)); + if(nHDCount2 < nTurnHD / 2) return FALSE; + ActionMoveToObject(oNearestEnemy, TRUE, 1.0f); + ai_UseFeat(oCreature, FEAT_TURN_UNDEAD, oCreature); + return TRUE; + } + return FALSE; +} +int ai_TryWhirlwindFeat(object oCreature) +{ + if(!GetHasFeat(FEAT_WHIRLWIND_ATTACK, oCreature)) return FALSE; + // Only worth using if there are 3+ targets. + if(AI_DEBUG) ai_Debug("0i_talents", "860", "WHIRLWIND : NumOfEnemies: " + IntToString(ai_GetNumOfEnemiesInGroup(oCreature, 3.0)) + "."); + // Shortened distance so its more effective(went from 5.0 to 2.0 and up to 3.0) + if(ai_GetNumOfEnemiesInGroup(oCreature, 3.0) < d3() + 1) return FALSE; + // * DO NOT WHIRLWIND if any of the targets are "large" or bigger + // * it seldom works against such large opponents. + // * Though its okay to use Improved Whirlwind against these targets + if((!GetHasFeat(FEAT_IMPROVED_WHIRLWIND, oCreature)) || + (GetCreatureSize(ai_GetNearestEnemy(oCreature, 1, 7, 7)) >= CREATURE_SIZE_LARGE && + GetCreatureSize(ai_GetNearestEnemy(oCreature, 2, 7, 7)) >= CREATURE_SIZE_LARGE)) + ai_UseFeat(oCreature, FEAT_WHIRLWIND_ATTACK, oCreature); + return TRUE; +} +int ai_TryWholenessOfBodyFeat(object oCreature) +{ + if(!GetHasFeat(FEAT_WHOLENESS_OF_BODY, oCreature)) return FALSE; + // Get when we are suppose to heal base off conversation with PC or + // on spawn generation. + int nHp = ai_GetPercHPLoss(oCreature); + if(nHp >= AI_HEALTH_WOUNDED) return FALSE; + ai_UseFeat(oCreature, FEAT_WHOLENESS_OF_BODY, oCreature); + return TRUE; +} +// ***************************************************************************** +// ******************** Try Physical Attack Talents **************************** +// ***************************************************************************** +// These functions try to find and use physical attack talents intelligently. + +void ai_ActionAttack(object oCreature, int nAction, object oTarget, int nInMelee = 0, int bPassive = FALSE, int nActionMode = 0) +{ + // If we are doing a ranged attack then check our position on the battlefield. + if(nAction == AI_LAST_ACTION_RANGED_ATK && ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nAction)) return; + ai_SetLastAction(oCreature, nAction); + SetLocalObject(oCreature, AI_ATTACKED_PHYSICAL, oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "894", GetName(oCreature) + " is attacking(" + IntToString(nAction) + + ") " + GetName(oTarget) + " Current Action: " + IntToString(GetCurrentAction(oCreature)) + + " Lastround Attacked Target: " + GetName(ai_GetAttackedTarget(oCreature)) + + " bPassive: " + IntToString(bPassive) + " nActionMode: " + IntToString(nActionMode)); + ActionAttack(oTarget, bPassive); + if(nActionMode == 0) ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); +} +void ai_FlyToAttacks(object oCreature, object oTarget) +{ + ai_TryWingAttacks(oCreature); + // If we don't do a Tail sweep attack then see if we can do a Tail slap! + if(!ai_TryTailSweepAttack(oCreature)) ai_TryTailSlap(oCreature); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); +} +void ai_FlyToTarget(object oCreature, object oTarget) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "908", GetName(OBJECT_SELF) + " is flying to " + GetName(oTarget) + "!"); + effect eFly = EffectDisappearAppear(GetLocation(oTarget)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFly, oCreature, 3.0f); + DelayCommand(4.0f, ai_FlyToAttacks(oCreature, oTarget)); + // Used to make creature wait before starting its next round. + SetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS, 5); +} +int ai_TryDragonBreathAttack(object oCreature, int nRound, object oTarget = OBJECT_INVALID) +{ + int nCnt = GetLocalInt(oCreature, "AI_DRAGONS_BREATH"); + if(AI_DEBUG) ai_Debug("0i_talents", "918", "Try Dragon Breath Attack: nRound(" + IntToString(nRound) + ")" + + " <= nCnt(" + IntToString(nCnt) + ")!"); + if(nRound <= nCnt) return FALSE; + talent tUse = GetCreatureTalentBest(TALENT_CATEGORY_DRAGONS_BREATH, 20, oCreature); + if(!GetIsTalentValid(tUse)) return FALSE; + if(oTarget == OBJECT_INVALID) + { + string sIndex = IntToString(ai_GetHighestMeleeIndexNotInAOE(oCreature)); + oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); + if(oTarget == OBJECT_INVALID) return FALSE; + } + SetLocalInt(oCreature, "AI_DRAGONS_BREATH", d4() + nRound); + ActionCastSpellAtObject(GetIdFromTalent(tUse), oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "1019", GetName(oCreature) + " breaths on " + GetName(oTarget) + "!"); + return TRUE; +} +void ai_DragonMeleeAttack(object oCreature, object oTarget, string sDmgDice, string sText) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "941", "oAttacker: " + GetName(oCreature) + + " oTarget: " + GetName(oTarget)); + int nDmg, nCheck, nAB = ai_GetCreatureAttackBonus(oCreature) - 5; + int nAC = GetAC(oTarget); + int nRoll = d20(); + string sHit; + // nCheck is a hit if nCheck > -1 and a miss if < 0; + if(nRoll == 20) nCheck = 20; + // We add one to the check so a equal result is still a hit. + else if(nRoll > 1) nCheck = nRoll + nAB - nAC + 1; + else nCheck == 0; + if(nCheck > 0) + { + nDmg = ai_RollDiceString(sDmgDice); + if(nCheck == 20) nDmg = nDmg * 2; + } + if(nCheck > 0) sHit = "*hit*"; + else sHit = "*miss*"; + string sMessage = ai_AddColorToText(GetName(oCreature) + "'s", AI_COLOR_LIGHT_MAGENTA) + + ai_AddColorToText(sText + "attacks " + GetName(oTarget) + " : " + sHit + " :(" + + IntToString(nRoll) + " + " + IntToString(nAB) + + " = " + IntToString(nRoll + nAB) + ")", AI_COLOR_DARK_ORANGE); + if(ai_GetIsCharacter(oTarget)) SendMessageToPC(oCreature, sMessage); + if(ai_GetIsCharacter(oTarget)) SendMessageToPC(oTarget, sMessage); + if(AI_DEBUG) ai_Debug("0i_talents", "965", "nAB: " + IntToString(nAB) + + " nAC: " + IntToString(nAC) + " nRoll: " + IntToString(nRoll) + + " nCheck: " + IntToString(nCheck) + " nDmg: " + IntToString(nDmg)); + if(nCheck <= 0) return; + // Apply any damage to the target! + effect eDmg = EffectDamage(nDmg, DAMAGE_TYPE_BLUDGEONING); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDmg, oTarget); +} +// Checks to see if a dragon can use its wings on a nearby enemy. +// Checks the right side and then the left side to see if it can attack. +int ai_TryWingAttacks(object oCreature) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "977", GetName(oCreature) + " is checking for wing Attacks!"); + // Only Medium size dragons can use thier wings in combat. + // We use HitDice to base size S:1-5, M:6-11, L:12-17, H:18-29, G:30-39, C:40+. + int nHitDice = GetHitDice(oCreature); + if(nHitDice <= 5) return FALSE; + int nDragonSize; + string sDmgDice, sMessage; + float fSize; + // Get the stats based on the size of the dragon. + if(nHitDice < 12) { fSize = 5.0f; nDragonSize = 3; sDmgDice = "1d4"; } // Medium + else if(nHitDice < 18) { fSize = 10.0f; nDragonSize = 4; sDmgDice = "1d6"; } // Large + else if(nHitDice < 30) { fSize = 10.0f; nDragonSize = 5; sDmgDice = "1d8"; } // Huge + else if(nHitDice < 40) { fSize = 15.0f; nDragonSize = 6; sDmgDice = "2d6"; } // Gargantuan + else { fSize = 15.0f; nDragonSize = 7; sDmgDice = "2d8"; } // Colossal + // Add half the dragons strength modifier. + int nDmg = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + if(nDmg > 0) sDmgDice = sDmgDice + "+" + IntToString(nDmg / 2); + if(AI_DEBUG) ai_Debug("0i_talents", "994", "nHitDice: " + IntToString(nHitDice) + + " nDragonSize: " + IntToString(nDragonSize) + + " sDmgDice: " + sDmgDice + " nDmg: " + IntToString(nDmg)); + // Get the closest enemy to our right wing. + location lWing = GetFlankingRightLocation(oCreature); + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fSize, lWing); + while(oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1002", "oTarget: " + GetName(oTarget)); + if(GetIsEnemy(oTarget, oCreature) && !GetIsDead(oTarget)) break; + oTarget = GetNextObjectInShape(SHAPE_SPHERE, fSize, lWing); + } + if(oTarget != OBJECT_INVALID) ai_DragonMeleeAttack(oCreature, oTarget, sDmgDice, " right wing "); + // Get the closest enemy to our left wing. + lWing = GetFlankingLeftLocation(oCreature); + oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fSize, lWing); + while(oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1012", "oTarget: " + GetName(oTarget)); + if(GetIsEnemy(oTarget, oCreature) && !GetIsDead(oTarget)) break; + oTarget = GetNextObjectInShape(SHAPE_SPHERE, fSize, lWing); + } + if(oTarget != OBJECT_INVALID) ai_DragonMeleeAttack(oCreature, oTarget, sDmgDice, " left wing "); + return TRUE; +} +// Looks behind the dragon to see if it can use it's tail slap on an enemy. +int ai_TryTailSlap(object oCreature) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1022", GetName(OBJECT_SELF) + " is checking for tail slap Attack!"); + // Only Large size dragons can use thier tail in combat. + // We use HitDice to base size S:1-5, M:6-11, L:12-17, H:18-29, G:30-39, C:40+. + int nHitDice = GetHitDice(oCreature); + if(nHitDice <= 11) return FALSE; + int nDragonSize; + string sDmgDice, sMessage; + float fSize; + // Get the stats based on the size of the dragon. + if(nHitDice < 12) { fSize = 5.0f; nDragonSize = 3; sDmgDice = "1d4"; } // Medium + else if(nHitDice < 18) { fSize = 10.0f; nDragonSize = 4; sDmgDice = "1d6"; } // Large + else if(nHitDice < 30) { fSize = 10.0f; nDragonSize = 5; sDmgDice = "1d8"; } // Huge + else if(nHitDice < 40) { fSize = 15.0f; nDragonSize = 6; sDmgDice = "2d6"; } // Gargantuan + else { fSize = 15.0f; nDragonSize = 7; sDmgDice = "2d8"; } // Colossal + // Add one and a half the dragons strength modifier. + int nDmg = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + if(nDmg > 0) sDmgDice = sDmgDice + "+" + IntToString(nDmg + nDmg / 2); + if(AI_DEBUG) ai_Debug("0i_talents", "1039", "nHitDice: " + IntToString(nHitDice) + + " nDragonSize: " + IntToString(nDragonSize) + + " sDmgDice: " + sDmgDice + " nDmg: " + IntToString(nDmg)); + // Get the closest enemy to our tail. + location lTail = GetBehindLocation(oCreature); + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fSize, lTail); + while(oTarget != OBJECT_INVALID) + { + if(GetIsEnemy(oTarget, oCreature) && !GetIsDead(oTarget)) break; + oTarget = GetNextObjectInShape(SHAPE_SPHERE, fSize, lTail); + } + if(oTarget != OBJECT_INVALID) ai_DragonMeleeAttack(oCreature, oTarget, sDmgDice, " tail ");\ + return TRUE; +} +void ai_CrushEffect(object oCreature, object oBaseTarget, int nHitDice) +{ + int nDragonSize, nAtkValue, nDC = ai_GetDragonDC(oCreature); + string sDmgDice, sMessage; + location lImpact = GetLocation(oBaseTarget); + float fSize; + // Get the stats based on the size of the dragon. + if(nHitDice < 30) { fSize = 15.0f; nDragonSize = 5; sDmgDice = "2d8"; } // Huge + else if(nHitDice < 40) { fSize = 25.0f; nDragonSize = 6; sDmgDice = "4d6"; } // Gargantuan + else { fSize = 45.0f; nDragonSize = 7; sDmgDice = "4d8"; } // Colossal + // Add the dragons strength modifier 1.5 times. + int nDmgBonus = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + if(nDmgBonus > 0) sDmgDice = sDmgDice + "+" + IntToString(nDmgBonus + nDmgBonus / 2); + // Dragon flies up and then crushes the area below it. + effect eDmg, eKnockDown = EffectKnockdown(); + effect eImpact = EffectVisualEffect(VFX_FNF_SCREEN_SHAKE); + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fSize, lImpact); + while(oTarget != OBJECT_INVALID) + { + if(ai_GetIsCharacter(oTarget)) DelayCommand(1.0, ApplyEffectToObject(DURATION_TYPE_INSTANT, eImpact, oTarget)); + // If they have evasion they automatically dodge the crush attack. + if(!GetHasFeat(FEAT_EVASION, oTarget) && oTarget != oCreature) + { + if(!ReflexSave(oTarget, nDC, SAVING_THROW_TYPE_NONE, oCreature)) + { + eDmg =EffectDamage(ai_RollDiceString(sDmgDice), DAMAGE_TYPE_BLUDGEONING); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDmg, oTarget); + sMessage = ai_AddColorToText(GetName(oCreature), AI_COLOR_LIGHT_MAGENTA) + + ai_AddColorToText(" crushes " + GetName(oTarget) + ".", AI_COLOR_DARK_ORANGE); + if(ai_GetIsCharacter(oTarget)) SendMessageToPC(oTarget, sMessage); + // Must be 3 sizes smaller to be affected by extra damage and knockdown. + if(nDragonSize - 2 < GetCreatureSize(oTarget)) + { + if(!GetIsImmune(oTarget, IMMUNITY_TYPE_KNOCKDOWN)) + { + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDmg, oTarget); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eKnockDown, oTarget, 6.0f); + } + } + } + } + else + { + if(ai_GetIsCharacter(oTarget)) + { + sMessage = ai_AddColorToText(GetName(oTarget), AI_COLOR_LIGHT_MAGENTA) + + ai_AddColorToText(" dodges the crush attack from " + GetName(oTarget) + ".", AI_COLOR_DARK_ORANGE); + if(ai_GetIsCharacter(oTarget)) SendMessageToPC(oTarget, sMessage); + } + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, fSize, lImpact); + } + // Now do normal attacks! + ai_FlyToAttacks(oCreature, oBaseTarget); +} +int ai_TryCrushAttack(object oCreature, object oTarget) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1110", GetName(OBJECT_SELF) + " is checking for crush Attack!"); + // Only Huge size dragons can use crush attack. + // We use HitDice to base size S:1-5, M:6-11, L:12-17, H:18-29, G:30-39, C:40+. + int nHitDice = GetHitDice(oCreature); + if(nHitDice <= 17) return FALSE; + int nCrush = GetLocalInt(oCreature, "0_DRAGON_CRUSH") - 1; + if(nCrush > 0) + { + SetLocalInt(oCreature, "0_DRAGON_CRUSH", nCrush); + return FALSE; + } + effect eFly = EffectDisappearAppear(GetLocation(oTarget)); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eFly, oCreature, 3.0f); + DelayCommand(4.0f, ai_CrushEffect(oCreature, oTarget, nHitDice)); + // Used to make creature wait before starting its next round. + SetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS, 5); + // We only crush every 3 rounds if we can. + SetLocalInt(oCreature, "0_DRAGON_CRUSH", 3); + return TRUE; +} +int ai_TryTailSweepAttack(object oCreature) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1132", GetName(oCreature) + " is checking for tail sweep Attack!"); + // Only Gargantuan size dragons can use tail sweep attack. + // We use HitDice to base size S:1-5, M:6-11, L:12-17, H:18-29, G:30-40, C:40+. + int nHitDice = GetHitDice(oCreature); + if(nHitDice <= 29) return FALSE; + int nSweep = GetLocalInt(oCreature, "0_DRAGON_SWEEP") - 1; + if(nSweep > 0) + { + SetLocalInt(oCreature, "0_DRAGON_SWEEP", nSweep); + return FALSE; + } + int nDragonSize, nAtkValue, nDC = ai_GetDragonDC(oCreature); + string sDmgDice, sMessage; + float fSize; + // Get the stats based on the size of the dragon. + if(nHitDice < 33) { fSize = 15.0f; nDragonSize = 6; sDmgDice = "2d6"; } // Gargantuan + else { fSize = 40.0f; nDragonSize = 7; sDmgDice = "2d8"; } // Colossal + location lImpact = GetBehindLocation(oCreature); + // We always sweep if we have the opportunity. + // Add the dragons strength modifier 1.5 times. + int nDmgBonus = GetAbilityModifier(ABILITY_STRENGTH, oCreature); + if(nDmgBonus > 0) sDmgDice = sDmgDice + "+" + IntToString(nDmgBonus + nDmgBonus / 2); + // Sweeps any creatures behind them. + effect eDmg; + effect eKnockDown = EffectKnockdown(); + object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fSize, lImpact); + while(oTarget != OBJECT_INVALID) + { + sMessage = ai_AddColorToText(GetName(oCreature), AI_COLOR_LIGHT_MAGENTA) + + ai_AddColorToText(" sweeps " + GetName(oTarget) + ".", AI_COLOR_ORANGE); + if(ai_GetIsCharacter(oTarget)) SendMessageToPC(oTarget, sMessage); + // If they have evasion they automatically dodge the sweep attack. + if(!GetHasFeat(FEAT_EVASION, oTarget) && oTarget != oCreature) + { + if(!ReflexSave(oTarget, nDC, SAVING_THROW_TYPE_NONE, oCreature)) + { + eDmg = EffectDamage(ai_RollDiceString(sDmgDice), DAMAGE_TYPE_BLUDGEONING); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eDmg, oTarget); + // Must be 4 sizes smaller to be affected by extra damage and knockdown. + if(nDragonSize - 3 < GetCreatureSize(oTarget)) + { + if(!GetIsImmune(oTarget, IMMUNITY_TYPE_KNOCKDOWN)) + { + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eKnockDown, oTarget, 12.0f); + } + } + } + } + oTarget = GetNextObjectInShape(SHAPE_SPHERE, fSize, lImpact); + } + // We only sweep every 3 rounds if we can. + SetLocalInt(oCreature, "0_DRAGON_SWEEP", 3); + return TRUE; +} +int ai_TrySneakAttack(object oCreature, int nInMelee, int bAlwaysAtk = TRUE) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1188", GetName(OBJECT_SELF) + " is checking for melee Sneak Attack!"); + if(!GetHasFeat(FEAT_SNEAK_ATTACK, oCreature)) return FALSE; + // Lets get the nearest target that is attacking someone besides me. + object oTarget = OBJECT_INVALID; + oTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) + { + string sIndex; + // Check if we have Mobility, Spring Attack or a good tumble. + // if we do then look for other targets besides who we are in melee with. + if(!nInMelee) sIndex = IntToString(ai_GetBestSneakAttackIndex(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk)); + // If there are few enemies then we can safely move around. + else if(nInMelee < 3 || ai_CanIMoveInCombat(oCreature)) + { + 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)); + oTarget = GetLocalObject(oCreature, AI_ENEMY + sIndex); + } + if(oTarget == OBJECT_INVALID) return FALSE; + int nRacialType = GetRacialType(oTarget); + if(nRacialType == RACIAL_TYPE_CONSTRUCT || nRacialType == RACIAL_TYPE_UNDEAD) return FALSE; + if(ai_GetHasEffectType(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)) return FALSE; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return TRUE; +} +int ai_TryRangedSneakAttack(object oCreature, int nInMelee) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1209", GetName(oCreature) + " is checking for a Ranged Sneak Attack!"); + // If we have Sneak Attack then we should be attacking targets that + // are busy fighting so we can get extra damage. + if(!GetHasFeat(FEAT_SNEAK_ATTACK, oCreature)) return FALSE; + object oTarget = OBJECT_INVALID; + oTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = GetLocalObject(oCreature, AI_ENEMY + IntToString(ai_GetBestSneakAttackIndex(oCreature))); + if(oTarget == OBJECT_INVALID) return FALSE; + int nRacialType = GetRacialType(oTarget); + if(nRacialType == RACIAL_TYPE_CONSTRUCT || nRacialType == RACIAL_TYPE_UNDEAD) return FALSE; + if(ai_GetHasEffectType(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)) return FALSE; + // If we have a target and are not within 30' then move within 30'. + if(GetDistanceToObject(oTarget) > AI_RANGE_CLOSE) ActionMoveToObject(oTarget, TRUE, AI_RANGE_CLOSE); + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return TRUE; +} +int ai_TryMeleeTalents(object oCreature, object oTarget) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1224", "Check category melee talents!"); + talent tUse = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_MELEE, 20, oCreature); + if(!GetIsTalentValid(tUse)) return FALSE; + int nId = GetIdFromTalent(tUse); + if(AI_DEBUG) ai_Debug("0i_talents", "1228", "TALENT_CATEGORY_MELEE_TALENTS nId: " + IntToString(nId)); + if(nId == FEAT_POWER_ATTACK) { if(ai_TryPowerAttackFeat(oCreature, oTarget)) return TRUE; } + else if(nId == FEAT_EXPERTISE) { if(ai_TryExpertiseFeat(oCreature)) return TRUE; } + else if(nId == FEAT_KNOCKDOWN) { if(ai_TryKnockdownFeat(oCreature, oTarget)) return TRUE; } + else if(nId == FEAT_SMITE_EVIL) { if(ai_TrySmiteEvilFeat(oCreature, oTarget)) return TRUE; } + else if(nId == FEAT_SMITE_GOOD) { if(ai_TrySmiteGoodFeat(oCreature, oTarget)) return TRUE; } + else if(nId == FEAT_IMPROVED_POWER_ATTACK) { if(ai_TryImprovedPowerAttackFeat(oCreature, oTarget)) return TRUE; } + else if(nId == FEAT_IMPROVED_EXPERTISE) { if(ai_TryImprovedExpertiseFeat(oCreature)) return TRUE; } + else if(nId == FEAT_FLURRY_OF_BLOWS) { if(ai_TryFlurryOfBlowsFeat(oCreature, oTarget)) return TRUE; } + else if(nId == FEAT_STUNNING_FIST) { if(ai_TryStunningFistFeat(oCreature, oTarget)) return TRUE; } + else if(nId == FEAT_SAP) { if(ai_TrySapFeat(oCreature, oTarget)) return TRUE; } + 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; } + return FALSE; +} +// ***************************************************************************** +// ***************************** TALENT SCRIPTS ****************************** +// ***************************************************************************** +// These functions do not fall into another section. + +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; +} +int ai_GetAssociateTalentMaxLevel(object oCreature, int nDifficulty) +{ + int nLevel = (ai_GetCharacterLevels(oCreature) + 1) / 2; + if(nLevel > 20) nLevel = 20; + int nMaxLevel = (nLevel * nDifficulty) / 20; + if(nMaxLevel < 1) nMaxLevel = 1; + if(AI_DEBUG) ai_Debug("0i_talents", "1267", "nLevel: " + IntToString(nLevel) + + " nMaxLevel: " + IntToString(nMaxLevel)); + return nMaxLevel; +} +int ai_GetHasTalent(object oCreature, int nTalent) +{ + string sCategory = Get2DAString("ai_spells", "Category", nTalent); + json jCategory = GetLocalJson(oCreature, sCategory); + if(JsonGetType(jCategory) == JSON_TYPE_NULL) return FALSE; + int nLevel, nSlot, nSlotIndex, nMaxSlotIndex, nSpell; + json jLevel, jTalent; + // Loop through nLevels looking for nTalent + while(nLevel <= 9) + { + // 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); + nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); + if(nSpell == nTalent) return TRUE; + nSlotIndex++; + } + } + nLevel++; + } + return FALSE; +} +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; + return OBJECT_INVALID; +} +int ai_UseBuffTalent(object oCreature, int nClass, int nLevel, int nSlot, int nSpell, int nType, object oTarget, object oItem) +{ + if(nType == AI_TALENT_TYPE_SPELL) + { + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + if(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot)) + { + ai_CastMemorizedSpell(oCreature, nClass, nLevel, nSlot, oTarget, TRUE); + return TRUE; + } + } + else if(GetSpellUsesLeft(oCreature, nClass, nSpell)) + { + ai_CastKnownSpell(oCreature, nClass, nSpell, oTarget, TRUE); + return TRUE; + } + } + else if(nType == AI_TALENT_TYPE_SP_ABILITY) + { + ActionCastSpellAtObject(nSpell, oTarget, 255, FALSE, 0, 0, TRUE, 255); + } + /* This will not work as there is no cheat option for using an item. + else if(nType == AI_TALENT_TYPE_ITEM) + { + int nBaseItem = GetBaseItemType(oItem); + if(!AI_BUFF_MONSTER_POTIONS && + (nBaseItem == BASE_ITEM_POTIONS || nBaseItem == BASE_ITEM_ENCHANTED_POTION)) return FALSE; + itemproperty ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + if(nIndex++ == nSlot) break; + ipProp = GetNextItemProperty(oItem); + } + // Cast items have the following: + // 1)Single_Use. + // 2-6) Charges/Use [Note: 7 is 0 charges per use]. + // 8-12) Uses/Day [Note: 13 is unlimited uses per day]. + // We set the slot to -1 to let the other function know we need this talent removed. + int nUses = GetItemPropertyCostTableValue(ipProp); + if(nUses == 1) jTalent = JsonArrayInsert(jTalent, JsonInt(-1), 4); + else if(nUses > 1 && nUses < 7) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1319", "Item charges: " + IntToString(GetItemCharges(oItem))); + int nCharges = GetItemCharges(oItem); + if(nUses == 6 && nCharges == 1 || nUses == 5 && nCharges < 4 || + nUses == 4 && nCharges < 6 || nUses == 3 && nCharges < 8 || + nUses == 2 && nCharges < 10) return FALSE; + } + else if(nUses > 7 && nUses < 13) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1327", "Item uses: " + IntToString(GetItemPropertyUsesPerDayRemaining(oItem, ipProp))); + int nPerDay = GetItemPropertyUsesPerDayRemaining(oItem, ipProp); + if(nUses == 8 && nPerDay == 1 || nUses == 9 && nPerDay < 4 || + nUses == 10 && nPerDay < 6 || nUses == 11 && nPerDay < 8 || + nUses == 12 && nPerDay < 10) return FASLE; + } + ActionUseItemOnObject(oItem, ipProp, oTarget, nSubIndex); + return TRUE; + } */ + return FALSE; +} +int ai_SpellRestricted(int nSpell) +{ + json jRSpells = GetLocalJson(GetModule(), AI_RULE_RESTRICTED_SPELLS); + int nIndex, nMaxIndex = JsonGetLength(jRSpells); + while(nIndex < nMaxIndex) + { + if(JsonGetInt(JsonArrayGet(jRSpells, nIndex)) == nSpell) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1703", IntToString(nSpell) + " is has been restricted and will be ignored!"); + return TRUE; + } + nIndex++; + } + 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) +{ + // 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) + { + int nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); + if(nSpellBuffDuration == 3) + { + 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; + } + } + json jCategory = GetLocalJson(oCreature, sCategory); + // With no jCategory then we make one with all 0-9 levels. + if(JsonGetType(jCategory) == JSON_TYPE_NULL) + { + jCategory = JsonArray(); + jCategory = JsonArrayInsert(jCategory, JsonArray(), 0); + int nNewLevel = 9; + while(nNewLevel > 0) + { + jCategory = JsonArrayInsert(jCategory, JsonArray()); + nNewLevel--; + } + } + // Get the current Level so we can save to it. + json jLevel = JsonArrayGet(jCategory, nJsonLevel); + 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)); + } + 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)); + } + 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)); + // 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); + } +} +// 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)); + jLevel = JsonArrayDel(jLevel, nSlotIndex); + if(AI_DEBUG) ai_Debug("0i_talents", "1402", "jLevel: " + JsonDump(jLevel, 2)); + jCategory = JsonArraySet(jCategory, nLevel, jLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "1404", "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)); + jCategory = JsonArrayDel(jCategory, nLevel); + if(AI_DEBUG) ai_Debug("0i_talents", "1412", "jCategory: " + JsonDump(jCategory, 2)); + SetLocalJson(oCreature, sCategory, jCategory); +} +void ai_SetCreatureSpellTalents(object oCreature, int bMonster) +{ + 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); + } + 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); + } + nSlot++; + } + nLevel--; + } + } + } + nClassPosition++; + nClass = GetClassByPosition(nClassPosition, oCreature); + } +} +void ai_SetCreatureSpecialAbilityTalents(object oCreature, int bMonster) +{ + 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(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); + } + nIndex++; + } + } +} +void ai_CheckItemProperties(object oCreature, object oItem, int bMonster, 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, 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, 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 nSpellImmunity 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) +{ + if(AI_DEBUG) ai_Debug("0i_talents", "1561", GetName(oCreature) + ": Setting Item Talents for combat."); + int bEquiped; + 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); + } + 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, TRUE); + oItem = GetItemInSlot(++nSlot, oCreature); + } + oItem = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature); + if(oItem != OBJECT_INVALID) ai_CheckItemProperties(oCreature, oItem, bMonster, TRUE); +} +void ai_SetCreatureTalents(object oCreature, int bMonster) +{ + 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; + SetLocalInt(oCreature, AI_TALENTS_SET, TRUE); + object oModule = GetModule(); + ai_Counter_Start(); + ai_SetCreatureSpellTalents(oCreature, bMonster); + ai_Counter_End(GetName(oCreature) + ": Spell Talents"); + ai_SetCreatureSpecialAbilityTalents(oCreature, bMonster); + ai_Counter_End(GetName(oCreature) + ": Special Ability Talents"); + DeleteLocalJson(oCreature, AI_TALENT_IMMUNITY); + ai_SetCreatureItemTalents(oCreature, bMonster); + ai_Counter_End(GetName(oCreature) + ": Item Talents"); + if(GetLocalInt(oModule, AI_RULE_SUMMON_COMPANIONS) && GetLocalInt(oModule, AI_RULE_PRESUMMON) && bMonster) + { + 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); +} +int ai_UseSpontaneousCureTalentFromCategory(object oCreature, string sCategory, int nInMelee, int nDamage, object oTarget = OBJECT_INVALID) +{ + // Get the saved category from oCreature. + json jCategory = GetLocalJson(oCreature, sCategory); + if(AI_DEBUG) ai_Debug("0i_talents", "2095", "jCategory: " + sCategory + " " + JsonDump(jCategory, 2)); + if(JsonGetType(jCategory) == JSON_TYPE_NULL) return FALSE; + int nLevel = 4; + // 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", "2100", AI_MAX_TALENT + sCategory + ": " + + IntToString(nMaxTalentLevel) + + " nLevel: " + IntToString(nLevel)); + if(nMaxTalentLevel < nLevel) nLevel = nMaxTalentLevel; + if(nLevel < 0 || nLevel > 5) nLevel = 4; + json jLevel, jTalent, jLevelSave; + int nTalentType, nTalentClass, nTalentSlot, nSpell; + int nSlotIndex, nMaxSlotIndex, nMaxNoTalentLevel, nSpellSave, nLevelSave, nSlotSave; + string sSpellName; + // Loop through nLevels down to nMinNoTalentLevel looking for the first talent + // (i.e. the highest or best?). + 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", "2116", "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", "2125", "nSlotIndex: " + IntToString(nSlotIndex) + + " jTalent Type: " + IntToString(JsonGetInt(JsonArrayGet(jTalent, 0)))); + nTalentType = JsonGetInt(JsonArrayGet(jTalent, 0)); + nTalentClass = JsonGetInt(JsonArrayGet(jTalent, 2)); + // We can only convert spells from the cleric class. + if(nTalentType == AI_TALENT_TYPE_SPELL && nTalentClass == CLASS_TYPE_CLERIC) + { + if(nLevel == 4) nSpell = SPELL_CURE_CRITICAL_WOUNDS; + else if(nLevel == 3) nSpell = SPELL_CURE_SERIOUS_WOUNDS; + else if(nLevel == 2) nSpell = SPELL_CURE_MODERATE_WOUNDS; + else if(nLevel == 1) nSpell = SPELL_CURE_LIGHT_WOUNDS; + else nSpell = 0; + if(AI_DEBUG) ai_Debug("0i_talents", "2137", "nSpell: " + IntToString(nSpell)); + if(nSpell) + { + if(ai_ShouldWeCastThisCureSpell(nSpell, nDamage)) + { + + nTalentSlot = JsonGetInt(JsonArrayGet(jTalent, 4)); + SetMemorizedSpellReady(oCreature, nTalentClass, nLevel, nTalentSlot, FALSE); + ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(ai_GetIsCharacter(oCreature)) ai_SendMessages(GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_MAGENTA, oCreature); + if(AI_DEBUG) ai_Debug("0i_talents", "2148", GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + "."); + ActionCastSpellAtObject(nSpell, oTarget, 255, TRUE); + return TRUE; + } + // Save the lowest level cure spell as we might need to cast it. + else if(nLevel < nLevelSave) + { + jLevelSave = jLevel; + nLevelSave = nLevel; + nSlotSave = nTalentSlot; + nSpellSave = nSpell; + } + } + } + nSlotIndex++; + } + } + else SetLocalInt(oCreature, AI_MAX_TALENT + sCategory, nLevel - 1); + nLevel--; + } + // Did we find a spell? If we did then use it. + if(nSpellSave) + { + if(AI_DEBUG) ai_Debug("0i_talents", "2171", GetName(oCreature) + " has cast the lowest level cure spell on " + GetName(oTarget) + "."); + SetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevelSave, nSlotSave, FALSE); + ai_RemoveTalent(oCreature, jCategory, jLevelSave, sCategory, nLevelSave, nSlotSave); + sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpellSave))); + if(ai_GetIsCharacter(oCreature)) ai_SendMessages(GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_MAGENTA, oCreature); + ActionCastSpellAtObject(nSpellSave, oTarget, 255, TRUE); + return TRUE; + } + return FALSE; +} +int ai_UseCreatureSpellTalent(object oCreature, json jLevel, json jTalent, string sCategory, int nInMelee, object oTarget = OBJECT_INVALID) +{ + // Check for polymorph, spells cannot be used while polymorphed. + if(GetAppearanceType(oCreature) != ai_GetNormalAppearance(oCreature)) return FALSE; + // Get the spells information so we can check if they still have it. + int nClass = JsonGetInt(JsonArrayGet(jTalent, 2)); + int nLevel = JsonGetInt(JsonArrayGet(jTalent, 3)); + int nSlot = JsonGetInt(JsonArrayGet(jTalent, 4)); + if(ai_IsSilenced(oCreature, JsonGetInt(JsonArrayGet(jTalent, 2)))) + { + if(GetMemorizedSpellMetaMagic(oCreature, nClass, nLevel, nSlot) != METAMAGIC_SILENT) + { + object oAOE = GetNearestObjectByTag("VFX_MOB_SILENCE", oCreature); + float fDistance = GetDistanceBetween(oAOE, oCreature); + if(fDistance != 0.0 && fDistance <= 4.0) + { + location lLocation = GetRandomLocation(GetArea(oCreature), oCreature, 5.0); + ai_ClearCreatureActions(); + if(AI_DEBUG) ai_Debug("0i_talents", "2225", GetName(oCreature) + " is moving out of a silence effect!"); + ActionMoveToLocation(lLocation, TRUE); + return TRUE; + } + else return FALSE; + } + } + 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) +{ + object oItem = StringToObject(JsonGetString(JsonArrayGet(jTalent, 2))); + int nItemType = GetBaseItemType(oItem); + // Check if the item is a potion since there are some special cases. + if(nItemType == BASE_ITEM_POTIONS || nItemType == BASE_ITEM_ENCHANTED_POTION) + { + // Potions cause attack of opportunities and this could be deadly! + // Removed for healing potions as that is one time you would use potions in melee. + if(sCategory != AI_TALENT_HEALING) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1925", "Using a non-healing potion nInMelee: " + IntToString(nInMelee)); + if(nInMelee > 1) return FALSE; + // Don't use potions on allies that are not within 3 meters. + if(GetDistanceBetween(oCreature, oTarget) > 3.1) return FALSE; + } + // For now we are allowing creatures to use "give" potions to others + // unless the player is using a healing potion and has party healing turned off. + else if(oCreature != oTarget && ai_GetAIMode(oCreature, AI_MODE_PARTY_HEALING_OFF)) return FALSE; + } + // Check for polymorph, only potions can be used while polymorphed. + else if(GetAppearanceType(oCreature) != ai_GetNormalAppearance(oCreature)) return FALSE; + else if(nItemType == BASE_ITEM_HEALERSKIT) + { + if(!GetLocalInt(GetModule(), AI_RULE_HEALERSKITS)) return FALSE; + if(oCreature != oTarget && ai_GetAIMode(oCreature, AI_MODE_PARTY_HEALING_OFF)) return FALSE; + if(AI_DEBUG) ai_Debug("0i_talents", "1724", "Using " + GetName(oItem) + " nInMelee: " + IntToString(nInMelee) + + " targeting: " + GetName(oTarget)); + ai_SetLastAction(oCreature, AI_LAST_ACTION_USED_ITEM); + ActionUseItemOnObject(oItem, GetFirstItemProperty(oItem), oTarget); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + // We also must check for stack size. + if(GetItemStackSize(oItem) == 1) JsonArrayInsertInplace(jTalent, JsonInt(-1), 4); + return TRUE; + } + 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) +{ + // 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 + ": " + + IntToString(nMaxTalentLevel) + + " nLevel: " + IntToString(nLevel)); + if(nMaxTalentLevel < nLevel) nLevel = nMaxTalentLevel; + if(nLevel < 0 || nLevel > 10) nLevel = 9; + json jLevel, jTalent; + int 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?). + 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) + + " 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", "2300", "nSlotIndex: " + IntToString(nSlotIndex) + + " jTalent Type: " + IntToString(JsonGetInt(JsonArrayGet(jTalent, 0)))); + nType = JsonGetInt(JsonArrayGet(jTalent, 0)); + if(bUseMagic) + { + if(nType == AI_TALENT_TYPE_SPELL) + { + nTalentUsed = ai_UseCreatureSpellTalent(oCreature, jLevel, jTalent, sCategory, nInMelee, oTarget); + // -1 means it was a memorized spell and we need to remove it. + if(nTalentUsed == -1) + { + ai_RemoveTalent(oCreature, jCategory, jLevel, sCategory, nLevel, nSlotIndex); + return TRUE; + } + else if(nTalentUsed == -2) + { + ai_RemoveTalentLevel(oCreature, jCategory, jLevel, sCategory, nLevel); + } + else if(nTalentUsed) 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); + return TRUE; + } + } + } + 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", "2337", "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; + } + } + //else if(nType == AI_TALENT_TYPE_FEAT) {} + nSlotIndex++; + } + } + else SetLocalInt(oCreature, AI_MAX_TALENT + sCategory, nLevel - 1); + nLevel--; + } + return FALSE; +} +int ai_UseTalent(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; +} +int ai_UseTalentOnObject(object oCreature, json jTalent, object oTarget, int nInMelee) +{ + int nClass, nLevel, nSlot, nMetaMagic, nDomain; + int nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); + int nType = JsonGetInt(JsonArrayGet(jTalent, 0)); + if(nType == AI_TALENT_TYPE_SPELL) + { + if(!ai_CastInMelee(oCreature, nSpell, nInMelee)) return FALSE; + nClass = JsonGetInt(JsonArrayGet(jTalent, 2)); + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + nLevel = JsonGetInt(JsonArrayGet(jTalent, 3)); + nSlot = JsonGetInt(JsonArrayGet(jTalent, 4)); + if(GetMemorizedSpellIsDomainSpell(oCreature, nClass, nLevel, nSlot) == 1) nDomain = nLevel; + else nDomain = 0; + nMetaMagic = GetMemorizedSpellMetaMagic(oCreature, nClass, nLevel, nSlot); + } + else + { + nMetaMagic = METAMAGIC_NONE; + nDomain = 0; + } + if(ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nSpell)) return TRUE; + } + else if(nType == AI_TALENT_TYPE_SP_ABILITY) + { + 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; + } + 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; + int nIndex, nSubIndex = 0; + nSlot = JsonGetInt(JsonArrayGet(jTalent, 4)); + itemproperty ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + if(nIndex++ == nSlot) break; + ipProp = GetNextItemProperty(oItem); + } + // Cast items have the following: + // 1)Single_Use. + // 2-6) Charges/Use [Note: 7 is 0 charges per use]. + // 8-12) Uses/Day [Note: 13 is unlimited uses per day]. + // We set the slot to -1 to let the other function know we need this talent removed. + int nUses = GetItemPropertyCostTableValue(ipProp); + if(nUses == 1) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1816", "Single Use item."); + if(AI_DEBUG) ai_Debug("0i_talents", "1817", "Stack size: " + IntToString(GetItemStackSize(oItem))); + // We also must check for stack size. + if(GetItemStackSize(oItem) == 1) JsonArrayInsertInplace(jTalent, JsonInt(-1), 4); + } + else if(nUses > 1 && nUses < 7) + { + int nCharges = GetItemCharges(oItem); + // If the item is equipable then do not use the last charge! + if(Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)) != "0x00000") + { + if(nCharges <= 7 - nUses) return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_talents", "1824", "Item charges: " + IntToString(nCharges)); + if(nCharges < (7 - nUses) * 2) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1829", "Stack size: " + IntToString(GetItemStackSize(oItem))); + // We also must check for stack size. + if(GetItemStackSize(oItem) == 1) JsonArrayInsertInplace(jTalent, JsonInt(-1), 4); + } + } + else if(nUses > 7 && nUses < 13) + { + int nPerDay = GetItemPropertyUsesPerDayRemaining(oItem, ipProp); + if(AI_DEBUG) ai_Debug("0i_talents", "1837", "Item uses: " + IntToString(nPerDay)); + if(nPerDay == 1) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1842", "Stack size: " + IntToString(GetItemStackSize(oItem))); + // We also must check for stack size. + if(GetItemStackSize(oItem) == 1) JsonArrayInsertInplace(jTalent, JsonInt(-1), 4); + } + } + // Lets not always use unlimited items! + else if(nUses == 7 || nUses == 13) + { + if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + } + ai_SetLastAction(oCreature, nSpell); + ActionUseItemOnObject(oItem, ipProp, oTarget, nSubIndex); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + if(AI_DEBUG) ai_Debug("0i_talents", "1850", GetName(oCreature) + " is using " + GetName(oItem) + " on " + GetName(oTarget)); + return TRUE; + } + if(AI_DEBUG) ai_Debug("0i_talents", "1853", "nMetaMagic: " + IntToString(nMetaMagic) + + " nDomain: " + IntToString(nDomain) + " nClass: " + IntToString(nClass)); + ai_SetLastAction(oCreature, nSpell); + ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain, 0, FALSE, nClass, FALSE); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + if(AI_DEBUG) + { + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + ai_Debug("0i_talents", "1859", GetName(oCreature) + " is casting " + sSpellName + " on " + GetName(oTarget)); + } + return TRUE; +} +int ai_UseTalentAtLocation(object oCreature, json jTalent, object oTarget, int nInMelee) +{ + int nSpell, nClass, nLevel, nSlot, nMetaMagic, nDomain; + int nType = JsonGetInt(JsonArrayGet(jTalent, 0)); + if(nType == AI_TALENT_TYPE_SPELL) + { + if(!ai_CastInMelee(oCreature, nSpell, nInMelee)) return FALSE; + nClass = JsonGetInt(JsonArrayGet(jTalent, 2)); + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); + nLevel = JsonGetInt(JsonArrayGet(jTalent, 3)); + nSlot = JsonGetInt(JsonArrayGet(jTalent, 4)); + if(GetMemorizedSpellIsDomainSpell(oCreature, nClass, nLevel, nSlot) == 1) nDomain = nLevel; + else nDomain = 0; + nMetaMagic = GetMemorizedSpellMetaMagic(oCreature, nClass, nLevel, nSlot); + } + else + { + nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); + nMetaMagic = METAMAGIC_NONE; + nDomain = 0; + } + } + else if(nType == AI_TALENT_TYPE_SP_ABILITY) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1888", GetName(oCreature) + " is using a special ability!"); + nSpell = JsonGetInt(JsonArrayGet(jTalent, 1)); + nClass = 255; + } + 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; + int nIndex; + int nSubIndex = JsonGetInt(JsonArrayGet(jTalent, 3));; + nSlot = JsonGetInt(JsonArrayGet(jTalent, 4)); + itemproperty ipProp = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProp)) + { + if(nIndex++ == nSlot) break; + ipProp = GetNextItemProperty(oItem); + } + // Cast items have the following: + // 1)Single_Use. + // 2-6) Charges/Use [Note: 7 is 0 charges per use]. + // 8-12) Uses/Day [Note: 13 is unlimited uses per day]. + // We set the slot to -1 to let the other function know we need this talent removed. + int nUses = GetItemPropertyCostTableValue(ipProp); + if(nUses == 1) JsonArrayInsertInplace(jTalent, JsonInt(-1), 4); + else if(nUses > 1 && nUses < 7) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1915", "Item charges: " + IntToString(GetItemCharges(oItem))); + int nCharges = GetItemCharges(oItem); + // If the item is equipable then do not use the last charge! + if(Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)) != "0x00000") + { + if(nCharges <= 7 - nUses) return FALSE; + } + if(AI_DEBUG) ai_Debug("0i_talents", "1824", "Item charges: " + IntToString(nCharges)); + if(nCharges < (7 - nUses) * 2) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1829", "Stack size: " + IntToString(GetItemStackSize(oItem))); + // We also must check for stack size. + if(GetItemStackSize(oItem) == 1) JsonArrayInsertInplace(jTalent, JsonInt(-1), 4); + } + } + else if(nUses > 7 && nUses < 13) + { + if(AI_DEBUG) ai_Debug("0i_talents", "1923", "Item uses: " + IntToString(GetItemPropertyUsesPerDayRemaining(oItem, ipProp))); + int nPerDay = GetItemPropertyUsesPerDayRemaining(oItem, ipProp); + if(nUses == 8 && nPerDay == 1 || nUses == 9 && nPerDay < 4 || + nUses == 10 && nPerDay < 6 || nUses == 11 && nPerDay < 8 || + nUses == 12 && nPerDay < 10) JsonArrayInsertInplace(jTalent, JsonInt(-1), 4); + } + // Lets not always use unlimited items! + else if(nUses == 7 || nUses == 13) + { + if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + } + if(ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nSpell)) return TRUE; + ai_SetLastAction(oCreature, nSpell); + ActionUseItemAtLocation(oItem, ipProp, GetLocation(oTarget), nSubIndex); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + if(AI_DEBUG) ai_Debug("0i_talents", "1934", GetName(oCreature) + " is using " + GetName(oItem) + " at a location."); + return TRUE; + } + if(ai_CheckCombatPosition(oCreature, oTarget, nInMelee, nSpell)) return TRUE; + ai_SetLastAction(oCreature, nSpell); + ActionCastSpellAtLocation(nSpell, GetLocation(oTarget), nMetaMagic, FALSE, 0, FALSE, nClass, FALSE, nDomain); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(AI_DEBUG) ai_Debug("0i_talents", "1943", GetName(oCreature) + " is casting " + sSpellName + " at a location!"); + return TRUE; +} +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))) + + " sCategory: " + sCategory); + if(sCategory == AI_TALENT_DISCRIMINANT_AOE) + { + //ai_Debug("0i_talents", "1953", "CompareLastAction: " + + // IntToString(ai_CompareLastAction(oCreature, nSpell))); + // If we used this spell talent last round then don't use it this round. + //if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + // Check to see if Disjunction should *not* be cast. + if(nSpell == SPELL_MORDENKAINENS_DISJUNCTION) + { + // Our master does not want us using any type of dispel! + if(ai_GetMagicMode(oCreature, AI_MAGIC_STOP_DISPEL)) return FALSE; + float fRange; + 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); + 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; + } + } + // These spells have a Range of Personal i.e. cast on themselves, and + // an Area of Effect of Colossal (10.0). + else if(nSpell == SPELL_FIRE_STORM || nSpell == SPELL_STORM_OF_VENGEANCE) + { + // Make sure we have enough enemies to use this on. + int nEnemies = ai_GetNumOfEnemiesInRange(oCreature, 10.0); + if(nEnemies < 2) return FALSE; + // Get the nearest target to check defenses on. + oTarget = ai_GetNearestTarget(oCreature, 10.0); + if(!ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || + ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; + if(ai_UseTalentAtLocation(oCreature, jTalent, oTarget, nInMelee)) return TRUE; + } + else if(nSpell == SPELL_UNDEATH_TO_DEATH) + { + float fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); + int nUndead = ai_GetRacialTypeCount(oCreature, RACIAL_TYPE_UNDEAD, fRange); + if(nUndead < 3) return FALSE; + oTarget = ai_GetLowestCRRacialTarget(oCreature, RACIAL_TYPE_UNDEAD, fRange); + } + // 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); + } + if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget) || + !ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || + ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; + } + else if(sCategory == AI_TALENT_INDISCRIMINANT_AOE) + { + //ai_Debug("0i_talents", "1991", "CompareLastAction: " + + // IntToString(ai_CompareLastAction(oCreature, nSpell))); + // If we used this spell talent last round then don't use it this round. + //if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + // These spells have a Range of Personal i.e. cast on themselves, and + // an Area of Effect of Colossal (10.0). + if(nSpell == SPELL_METEOR_SWARM) + { + // Make sure we have enough enemies and few allies to hit. + int nAllies = ai_GetNumOfAlliesInGroup(oCreature, 10.0); + int nEnemies = ai_GetNumOfEnemiesInRange(oCreature, 10.0); + if(nAllies > 1 || nEnemies < 2) return FALSE; + // Get the nearest target to check defenses on. + oTarget = ai_GetNearestTarget(oCreature, 10.0); + if(!ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || + ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; + if(ai_UseTalentAtLocation(oCreature, jTalent, oCreature, nInMelee)) return TRUE; + } + // Get a target for indiscriminant spells if one is not already set. + if(oTarget == OBJECT_INVALID) + { + float fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); + oTarget = ai_CheckForGroupedTargetNotInAOE(oCreature, fRange); + // 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); + if(AI_DEBUG) ai_Debug("0i_talents", "2084", "Num of Allies in range: " + IntToString(nAllies)+ + " < nRoll: " + IntToString(nRoll)); + if(nAllies >= nRoll) return FALSE; + } + if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget) || + !ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || + ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; + //********************************************************************** + //********** These spells are checked after picking a target *********** + //********************************************************************** + // Check if the Sleep spells are being used appropriately. + if(nSpell == SPELL_SLEEP) + { + if(GetHitDice(oTarget) > 4) return FALSE; + } + // Lets only use silence on casters. + else if(nSpell == SPELL_SILENCE) + { + if(!ai_CheckClassType(oTarget, AI_CLASS_TYPE_CASTER)) + { + oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER); + if(oTarget == OBJECT_INVALID) return FALSE; + } + } + } + else if(sCategory == AI_TALENT_RANGED) + { + //ai_Debug("0i_talents", "2045", "CompareLastAction: " + + // IntToString(ai_CompareLastAction(oCreature, nSpell))); + // If we used this spell talent last round then don't use it this round. + //if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + // Check to see if Dispel Magic and similar spells should *not* be cast + if(nSpell == SPELL_DISPEL_MAGIC || nSpell == SPELL_LESSER_DISPEL || + nSpell == SPELL_GREATER_DISPELLING) + { + // Our master does not want us using any type of dispel! + if(ai_GetMagicMode(oCreature, AI_MAGIC_STOP_DISPEL)) return FALSE; + float fRange; + if(nInMelee) fRange = AI_RANGE_MELEE; + else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); + // 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(!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 || + nSpell == SPELL_CHARM_PERSON) + { + if(oTarget != OBJECT_INVALID) + { + int nRaceType = GetRacialType(oTarget); + if(AI_DEBUG) ai_Debug("0i_talents", "2075", " Person Spell race: " + IntToString(nRaceType)); + if((nRaceType > 6 && nRaceType < 12) || nRaceType > 15) oTarget = OBJECT_INVALID; + } + if(oTarget == OBJECT_INVALID) + { + float fRange; + if(nInMelee) fRange = AI_RANGE_MELEE; + else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); + oTarget = ai_GetNearestRacialTarget(oCreature, AI_RACIAL_TYPE_HUMANOID, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; + } + } + else if(nSpell == SPELL_HOLD_ANIMAL || nSpell == SPELL_DOMINATE_ANIMAL) + { + if(oTarget != OBJECT_INVALID) + { + if(GetRacialType(oTarget) != RACIAL_TYPE_ANIMAL) oTarget = OBJECT_INVALID; + } + if(oTarget == OBJECT_INVALID) + { + float fRange; + if(nInMelee) fRange = AI_RANGE_MELEE; + else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); + oTarget = ai_GetNearestRacialTarget(oCreature, AI_RACIAL_TYPE_ANIMAL_BEAST, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; + } + } + // Get a target for ranged 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_GetSpellTargetBasedOnSaves(oCreature, nSpell, fRange); + } + if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget) || + !ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || + ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; + //********************************************************************** + //********** These spells are checked after picking a target *********** + //********************************************************************** + // Don't use Domination spells on players! They don't work. + if((nSpell == SPELL_DOMINATE_MONSTER || nSpell == SPELL_DOMINATE_PERSON)) + { + if(ai_GetIsCharacter(oTarget)) return FALSE; + } + // Check to see if they have the shield spell up. + else if(nSpell == SPELL_MAGIC_MISSILE) + { + if(GetHasSpellEffect(SPELL_SHIELD, oTarget)) return FALSE; + } + // Scare only works on 5 hitdice or less. + else if(nSpell == SPELL_SCARE) + { + if(GetHitDice(oTarget) > 5) return FALSE; + } + // Don't use drown against nonliving opponents. + else if(nSpell == SPELL_DROWN) + { + if(ai_IsNonliving(GetRacialType(oTarget))) return FALSE; + } + // Don't use Power Word Kill on Targets with more than 100hp + else if(nSpell == SPELL_POWER_WORD_KILL) + { + if(GetCurrentHitPoints(oTarget) <= 100) return FALSE; + } + } + else if(sCategory == AI_TALENT_TOUCH) + { + //ai_Debug("0i_talents", "2139", "CompareLastAction: " + + // IntToString(ai_CompareLastAction(oCreature, nSpell))); + // If we used this spell talent last round then don't use it this round. + //if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + // Get a target for touch spells if one is not already set. + if(oTarget == OBJECT_INVALID) + { + oTarget = ai_GetSpellTargetBasedOnSaves(oCreature, nSpell, AI_RANGE_MELEE); + } + if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget) || + !ai_CastOffensiveSpellVsTarget(oCreature, oTarget, nSpell) || + ai_CreatureImmuneToEffect(oCreature, oTarget, nSpell)) return FALSE; + } + else if(sCategory == AI_TALENT_HEALING) + { + int nHpLost = ai_GetPercHPLoss(oTarget); + // If the target is bloody then just use the best we have! + if(nHpLost > AI_HEALTH_BLOODY) + { + // Make sure we should use a mass heal on us or an ally! + // Two allies need healing or one is almost dead to use mass heal! + if(nSpell == SPELL_MASS_HEAL) + { + int bWoundedAlly; + object oAlly = ai_GetNearestAlly(oTarget); + if(oAlly != OBJECT_INVALID) + { + // If we don't have a nearby ally that needs healed then skip. + if(ai_GetPercHPLoss(oAlly) > AI_HEALTH_WOUNDED || + GetDistanceBetween(oCreature, oAlly) > 9.0f) return FALSE; + } + } + // Make sure they have taken enough damage. + int nHpDmg = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); + if(!ai_ShouldWeCastThisCureSpell(nSpell, nHpDmg)) return FALSE; + } + } + else if(sCategory == AI_TALENT_ENHANCEMENT) + { + if(AI_DEBUG) ai_Debug("0i_talents", "2713", "CompareLastAction: " + + IntToString(ai_CompareLastAction(oCreature, nSpell))); + // If we used this spell talent last round then don't use it this round. + if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + if(nSpell == SPELL_INVISIBILITY || nSpell == SPELL_SANCTUARY) + { + // Lets not run past an enemy to cast an enhancement unless we have + // the ability to move in combat, bad tactics! + float fRange; + if(ai_CanIMoveInCombat(oCreature)) fRange = AI_RANGE_PERCEPTION; + else + { + fRange = GetDistanceBetween(oCreature, GetLocalObject(oCreature, AI_ENEMY_NEAREST)) - 3.0f; + // Looks bad when your right next to an ally, but technically the enemy is closer. + if(fRange < AI_RANGE_MELEE) fRange = AI_RANGE_MELEE; + } + oTarget = ai_GetAllyToHealTarget(oCreature, fRange); + if(oTarget != OBJECT_INVALID) + { + int nHp = ai_GetPercHPLoss(oTarget); + int nHpLimit = ai_GetHealersHpLimit(oCreature); + if(nHp > nHpLimit) return FALSE; + } + } + if(nSpell == SPELL_PRAYER) + { + int nEnemies = ai_GetNumOfEnemiesInRange(oCreature, 10.0); + int nAllies = ai_GetNumOfAlliesInGroup(oCreature, 10.0); + if(nEnemies + nAllies < 5) return FALSE; + oTarget = oCreature; + } + // Since haste does not have an effect when it comes from items when we + // check for item properties we set this variable so we know they have it. + else if(nSpell == SPELL_HASTE && GetLocalInt(oCreature, sIPHasHasteVarname)) return FALSE; + // Only reason to cast Ultravision(Darkvision) in combat is if a Darkness + // spell is nearby. + else if(nSpell == SPELL_DARKVISION) + { + int nCnt = 1, bCastSpell; + string sAOEType; + object oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT, oCreature, nCnt); + while(oAOE != OBJECT_INVALID && GetDistanceBetween(oCreature, oAOE) <= AI_RANGE_PERCEPTION) + { + // AOE's have the tag set to the "LABEL" in vfx_persistent.2da + sAOEType = GetTag(oAOE); + if(AI_DEBUG) ai_Debug("0i_talents", "2759", "Ultravision check; AOE tag: " + sAOEType); + if(sAOEType == "VFX_PER_DARKNESS") + { + if(!GetHasFeat(FEAT_DARKVISION)) bCastSpell = TRUE; + break; + } + oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT, oCreature, ++nCnt); + } + if(!bCastSpell) return FALSE; + } + // Get a target for enhancement spells if one is not already set. + if(oTarget == OBJECT_INVALID) + { + // Get talents range and target. + float fRange = ai_GetSpellRange(nSpell); + // Personal spell + if(fRange == 0.1f) oTarget = oCreature; + // Range/Touch spell + else oTarget = ai_GetAllyBuffTarget(oCreature, nSpell, fRange); + } + if(AI_DEBUG) ai_Debug("0i_talents", "2260", " oTarget: " + GetName(oTarget) + + " HasSpellEffect: " + IntToString(GetHasSpellEffect(nSpell, oTarget))); + if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget)) return FALSE; + //********************************************************************** + //********** These spells are checked after picking a target *********** + //********************************************************************** + // Weapon enhancing spells only work on melee weapons! + if(nSpell == SPELL_MAGIC_WEAPON || nSpell == SPELL_GREATER_MAGIC_WEAPON || + nSpell == SPELL_BLESS_WEAPON || nSpell == SPELL_FLAME_WEAPON || + nSpell == SPELL_DARKFIRE) + { + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); + if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; + } + // Should we ignore associates? + if(ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && + GetAssociateType(oTarget) > 1) return FALSE; + } + else if(sCategory == AI_TALENT_PROTECTION) + { + if(AI_DEBUG) ai_Debug("0i_talents", "2281", "CompareLastAction: " + + IntToString(ai_CompareLastAction(oCreature, nSpell))); + // If we used this spell talent last round then don't use it this round. + if(ai_CompareLastAction(oCreature, nSpell)) return FALSE; + // Stone bones only effects the undead. + if(nSpell == SPELL_STONE_BONES) + { + if(oTarget != OBJECT_INVALID) + { + if(GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) oTarget = OBJECT_INVALID; + } + if(oTarget == OBJECT_INVALID) + { + float fRange; + if(nInMelee) fRange = AI_RANGE_MELEE; + else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); + oTarget = ai_GetNearestRacialTarget(oCreature, RACIAL_TYPE_UNDEAD, fRange); + if(oTarget == OBJECT_INVALID) return FALSE; + } + } + else if(nSpell == SPELL_MAGIC_FANG) + { + oTarget = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCreature); + if(oTarget == OBJECT_INVALID) return FALSE; + } + // Lets see if we should cast resistances in our current situation, + // lets check for enemy casters that may have energy damaging spells, or energy weapons. + else if(nSpell == SPELL_ENDURE_ELEMENTS || nSpell == SPELL_PROTECTION_FROM_ELEMENTS || + nSpell == SPELL_RESIST_ELEMENTS || nSpell == SPELL_ENERGY_BUFFER) + { + int bCastSpell; + object oEnemy = ai_GetEnemyAttackingMe(oCreature); + if(oEnemy != OBJECT_INVALID) + { + object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oEnemy); + if(oWeapon == OBJECT_INVALID) oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oEnemy); + if(oWeapon == OBJECT_INVALID) oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oEnemy); + if(AI_DEBUG) ai_Debug("0i_talents", "2812", GetName(oEnemy) + " is using weapon: " + GetName(oWeapon)); + if(oWeapon != OBJECT_INVALID) + { + itemproperty nProperty = GetFirstItemProperty(oWeapon); + while(GetIsItemPropertyValid(nProperty)) + { + if(GetItemPropertyType(nProperty) == ITEM_PROPERTY_DAMAGE_BONUS) + { + int nSubType = GetItemPropertySubType(nProperty); + if(AI_DEBUG) ai_Debug("0i_talents", "2821", GetName(oWeapon) + " has PropertySubType: " + + IntToString(nSubType) + " If equals [6,7,9,10,13] don't cast!"); + if(nSubType == 6 || nSubType == 7 || nSubType == 9 || + nSubType == 10 || nSubType == 13) + { + bCastSpell = TRUE; + break; + } + } + nProperty = GetNextItemProperty(oWeapon); + } + } + } + if(ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER) != OBJECT_INVALID) bCastSpell = TRUE; + if(!bCastSpell) return FALSE; + } + // Get a target for protection spells if one is not already set. + if(oTarget == OBJECT_INVALID) + { + // Get talents range and target. + float fRange = ai_GetSpellRange(nSpell); + // Personal spell + if(fRange == 0.1f) oTarget = oCreature; + // Range/Touch spell + else oTarget = ai_GetAllyBuffTarget(oCreature, nSpell, fRange); + } + if(oTarget == OBJECT_INVALID || GetHasSpellEffect(nSpell, oTarget)) return FALSE; + //********************************************************************** + //********** These spells are checked after picking a target *********** + //********************************************************************** + // Don't double up Stoneskin, Ghostly visage, or Ethereal visage. + if(nSpell == SPELL_GHOSTLY_VISAGE || nSpell == SPELL_ETHEREAL_VISAGE || + nSpell == SPELL_STONESKIN) + { + if(GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, oTarget) || + GetHasSpellEffect(SPELL_STONESKIN, oTarget) || + GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, oTarget)) return FALSE; + } + // Don't use displacement if we are invisible! + else if(nSpell == SPELL_DISPLACEMENT) + { + if(GetHasSpellEffect(SPELL_INVISIBILITY, oTarget) || + GetHasSpellEffect(SPELL_IMPROVED_INVISIBILITY, oTarget) || + GetHasSpellEffect(SPELL_INVISIBILITY_SPHERE, oTarget) || + GetHasSpellEffect(SPELL_DISPLACEMENT, oTarget)) return FALSE; + } + // Should we ignore associates? + if(ai_GetAIMode(oCreature, AI_MODE_IGNORE_ASSOCIATES) && + GetAssociateType(oTarget) > 1) return FALSE; + } + else if(sCategory == AI_TALENT_SUMMON) + { + if(GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCreature) != OBJECT_INVALID) return FALSE; + if(oTarget == OBJECT_INVALID) + { + /* Removed for now, summons creature in location that enemy was... looks bad. + float fRange; + if(nInMelee) fRange = AI_RANGE_MELEE; + else fRange = ai_GetOffensiveSpellSearchRange(oCreature, nSpell); + // Select lowest enemy combat target for summons. + oTarget = ai_GetLowestCRTarget(oCreature, fRange); + if(oTarget == OBJECT_INVALID) oTarget = oCreature; + */ + oTarget = oCreature; + if(ai_UseTalentAtLocation(oCreature, jTalent, oTarget, nInMelee)) + { + DelayCommand(4.0, ai_NameAssociate(oCreature, ASSOCIATE_TYPE_SUMMONED, "")); + return TRUE; + } + } + } + else if(sCategory == AI_TALENT_CURE) + { + } + if(ai_UseTalentOnObject(oCreature, jTalent, oTarget, nInMelee)) return TRUE; + return FALSE; +} diff --git a/_module/nss/0i_time.nss b/_module/nss/0i_time.nss new file mode 100644 index 00000000..3052ea67 --- /dev/null +++ b/_module/nss/0i_time.nss @@ -0,0 +1,95 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_time +//////////////////////////////////////////////////////////////////////////////// + Include script for handling all time functions for the server. + + Lokey's functions: +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. +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); + +int GetCurrentTimeInSeconds() +{ + string stmt = "SELECT strftime('%s','now');"; + sqlquery sqlQuery = SqlPrepareQueryObject(GetModule(), stmt); + SqlStep(sqlQuery); + return SqlGetInt(sqlQuery, 0); +} +string GetCurrentDateTime() +{ + string stmt = "SELECT datetime('now', 'localtime')"; + sqlquery sqlQuery = SqlPrepareQueryObject(GetModule(), stmt); + 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')"); + SqlBindString(query, "@format", 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"); +} +string SQLite_GetSystemTime() +{ + return SQLite_GetFormattedSystemTime("%H:%M:%S"); +} diff --git a/_module/nss/ai_a_ambusher.nss b/_module/nss/ai_a_ambusher.nss new file mode 100644 index 00000000..f3f424b6 --- /dev/null +++ b/_module/nss/ai_a_ambusher.nss @@ -0,0 +1,105 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_ambusher +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to ambush creatures by hiding or turning invisible. + OBJECT_SELF is the creature running the ai. + * This assumes we are not invisible since the ai_a_invisible script should fire if we are. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + if(AI_DEBUG) ai_Debug("ai_a_ambusher", "19", GetName(oCreature) + " is using ambusher tactics: " + + " oNearestEnemy: " + GetName(oNearestEnemy) + " fDistance: " + + FloatToString(GetDistanceBetween(oNearestEnemy, oCreature), 0, 2)); + if(GetDistanceBetween(oNearestEnemy, oCreature) > AI_RANGE_CLOSE) + { + // Has our master told us to not use magic? + if(!ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) + { + // If can turn invisible then we should probably do that! + if(ai_UseTalent(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY_SPHERE, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_SANCTUARY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_ETHEREALNESS, oCreature)) return; // Greater Sanctuary + if(ai_UseTalent(oCreature, SPELLABILITY_AS_IMPROVED_INVISIBLITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELLABILITY_AS_INVISIBILITY, oCreature)) return; + } + } + // Check the battle field to see if anyone see us? + int nEnemyIndex = ai_GetNearestIndexThatSeesUs(oCreature); + // If seen, can we try to hide now? + if(nEnemyIndex) + { + // Check for an attacker and can they see through invisibility? + object oAttacker = ai_GetEnemyAttackingMe(oCreature); + int bCanSeeInvisible; + if(oAttacker != OBJECT_INVALID) + { + bCanSeeInvisible = ai_GetHasEffectType(oAttacker, EFFECT_TYPE_SEEINVISIBLE); + if(!bCanSeeInvisible) bCanSeeInvisible = ai_GetHasEffectType(oAttacker, EFFECT_TYPE_TRUESEEING); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_5_FEET, oCreature); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_10_FEET, oCreature); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_60_FEET, oCreature); + } + if(!bCanSeeInvisible) + { + if(GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT, oCreature)) + { + if(!GetActionMode(oCreature, ACTION_MODE_STEALTH)) + { + if(AI_DEBUG) ai_Debug("ai_a_ambusher", "55", GetName(oCreature) + " is using hide in plain sight!"); + ClearAllActions(TRUE); + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + return; + } + } + // Does not have hide in plain sight. + else + { + string sEnemyIndex = IntToString(nEnemyIndex); + float fEnemyDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sEnemyIndex); + if(AI_DEBUG) ai_Debug("ai_a_ambusher", "66", "fDistance: " + FloatToString(fEnemyDistance, 0, 2)); + if(fEnemyDistance > 20.0) + { + int bTried = GetLocalInt(oCreature, AI_TRIED_TO_HIDE); + if(!bTried) + { + // Move away so we can hide. + if(AI_DEBUG) ai_Debug("ai_a_ambusher", "73", GetName(oCreature) + " is trying to move away to hide!"); + SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + object oEnemy = GetLocalObject(oCreature, AI_ENEMY + sEnemyIndex); + ActionMoveAwayFromObject(oEnemy, TRUE, AI_RANGE_BATTLEFIELD); + SetLocalInt(oCreature, AI_TRIED_TO_HIDE, 3); + return; + } + else SetLocalInt(oCreature, AI_TRIED_TO_HIDE, GetLocalInt(oCreature, AI_TRIED_TO_HIDE) - 1); + } + // We have been seen by an enemy near us so drop stealth. + else SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + } + } + // The enemy can see through stealth so lets drop it. + else SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + } + // We are not in stealth mode so and no one sees us so lets hide. + else if(!GetActionMode(oCreature, ACTION_MODE_STEALTH)) + { + // Use any hiding talents we have + if(AI_DEBUG) ai_Debug("ai_a_ambusher", "97", GetName(oCreature) + " is trying to hide!"); + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + SetLocalInt(oCreature, AI_TRIED_TO_HIDE, 3); + return; + } + // If we have givin up on stealth do our normal actions. + string sScript = GetLocalString(oCreature, AI_DEFAULT_SCRIPT); + if(sScript == "ai_a_ambusher" || sScript == "") sScript = "ai_a_default"; + if(AI_DEBUG) ai_Debug("ai_a_ambusher", "101", "Executing Script: " + sScript); + ExecuteScript(sScript, oCreature); +} diff --git a/_module/nss/ai_a_atk_casters.nss b/_module/nss/ai_a_atk_casters.nss new file mode 100644 index 00000000..362ba4b2 --- /dev/null +++ b/_module/nss/ai_a_atk_casters.nss @@ -0,0 +1,159 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_atk_casters +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to the nearest casting creatures. + OBJECT_SELF is the creature running the ai. + Our actions. + 1 - Get nearest enemy. + 2 - Check for healing and curing first. + 3 - Check moral if wounded and this is a simple+ battle. + 4 - Check for a magical ranged attack if not in melee and a difficult+ battle. + 5 - Check for a buff or summons if this is a difficult+ battle. + 6 - Check for a Class ability and an offensive spell if this is a simple+ battle. + 7 - Check for a physical attack. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, 0, oTarget)) return; + 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) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + int bAlwaysAtk = !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK); + if(AI_DEBUG) ai_Debug("ai_a_atk_casters", "80", "Check for ranged attack on nearest casting enemy!"); + // ************************** Ranged feat attacks ************************** + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + !ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && + ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + // Lets pick off the nearest targets first. + if(!nInMelee) + { + 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); + } + 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) + { + if(ai_TryRapidShotFeat(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; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + if(AI_DEBUG) ai_Debug("ai_a_atk_casters", "119", "Check for melee attack on nearest enemy!"); + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee, bAlwaysAtk)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) + { + object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + if(oPCTarget == OBJECT_INVALID) + { + // Are we in melee? If so try to get the nearest enemy in melee. + if(nInMelee > 0) + { + 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 not then lets go find someone to attack! + else + { + // 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); + } + } + } + // We might not have a target this is fine as sometimes we don't want to attack! + if(AI_DEBUG) ai_Debug("ai_a_atk_casters", "149", GetName(oTarget) + " is the nearest target for melee combat!"); + // If we don't find a target then we don't want to fight anyone! + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + if(AI_DEBUG) ai_Debug("ai_a_atk_casters", "154", "Do melee attack against (caster/nearest): " + GetName(oTarget) + "!"); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} + diff --git a/_module/nss/ai_a_atk_nearest.nss b/_module/nss/ai_a_atk_nearest.nss new file mode 100644 index 00000000..1261dc7a --- /dev/null +++ b/_module/nss/ai_a_atk_nearest.nss @@ -0,0 +1,80 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_atk_nearest +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to the nearest target. + OBJECT_SELF is the creature running the ai. + Our actions. + 1 - Get nearest enemy. + 2 - Check for healing and curing first. + 3 - Check moral if wounded and this is a simple+ battle. + 4 - Check for a magical ranged attack if not in melee and a difficult+ battle. + 5 - Check for a buff or summons if this is a difficult+ battle. + 6 - Check for a Class ability and an offensive spell if this is a simple+ battle. + 7 - Check for a physical attack. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, 0, oTarget)) return; + 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) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + ai_DoPhysicalAttackOnNearest(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); +} + diff --git a/_module/nss/ai_a_atk_warrior.nss b/_module/nss/ai_a_atk_warrior.nss new file mode 100644 index 00000000..4821e53e --- /dev/null +++ b/_module/nss/ai_a_atk_warrior.nss @@ -0,0 +1,159 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_atk_warrior +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to the nearest casting creatures. + OBJECT_SELF is the creature running the ai. + Our actions. + 1 - Get nearest enemy. + 2 - Check for healing and curing first. + 3 - Check moral if wounded and this is a simple+ battle. + 4 - Check for a magical ranged attack if not in melee and a difficult+ battle. + 5 - Check for a buff or summons if this is a difficult+ battle. + 6 - Check for a Class ability and an offensive spell if this is a simple+ battle. + 7 - Check for a physical attack. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, 0, oTarget)) return; + 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) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + int bAlwaysAtk = !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK); + if(AI_DEBUG) ai_Debug("0i_actions", "496", "Check for ranged attack on nearest casting enemy!"); + // ************************** Ranged feat attacks ************************** + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + !ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && + ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + // Lets pick off the nearest targets first. + if(!nInMelee) + { + 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); + } + 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) + { + if(ai_TryRapidShotFeat(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; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + if(AI_DEBUG) ai_Debug("ai_a_atk_warrior", "119", "Check for melee attack on nearest enemy!"); + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee, bAlwaysAtk)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) + { + object oPCTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + if(oPCTarget == OBJECT_INVALID) + { + // Are we in melee? If so try to get the nearest enemy in melee. + if(nInMelee > 0) + { + 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 not then lets go find someone to attack! + else + { + // 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); + } + } + } + // We might not have a target this is fine as sometimes we don't want to attack! + if(AI_DEBUG) ai_Debug("ai_a_atk_warrior", "149", GetName(oTarget) + " is the nearest target for melee combat!"); + // If we don't find a target then we don't want to fight anyone! + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + if(AI_DEBUG) ai_Debug("ai_a_atk_warrior", "154", "Do melee attack against (caster/nearest): " + GetName(oTarget) + "!"); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} + diff --git a/_module/nss/ai_a_barbarian.nss b/_module/nss/ai_a_barbarian.nss new file mode 100644 index 00000000..12521c93 --- /dev/null +++ b/_module/nss/ai_a_barbarian.nss @@ -0,0 +1,87 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_barbarian +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Barbarian class. + OBJECT_SELF is the creature 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); + object oTarget; + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature)) + { + //************************* HEALING & CURES ************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // ************************ CLASS FEATURES ************************* + if(ai_TryBarbarianRageFeat(oCreature)) return; + // ************************* SPELL TALENTS ************************* + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // ************************* 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ Ranged feat attacks ************************ + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + } + // *************************** Melee feat attacks ************************** + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_bard.nss b/_module/nss/ai_a_bard.nss new file mode 100644 index 00000000..975bac2e --- /dev/null +++ b/_module/nss/ai_a_bard.nss @@ -0,0 +1,83 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_bard +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Bard class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryBardSongFeat(oCreature)) return; + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget = OBJECT_INVALID; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_cleric.nss b/_module/nss/ai_a_cleric.nss new file mode 100644 index 00000000..5de1cc26 --- /dev/null +++ b/_module/nss/ai_a_cleric.nss @@ -0,0 +1,102 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_cleric +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Cleric class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + // ************************** CLASS FEATURES *************************** + // Turning is basically a powerful AOE so treat it like one. + if(ai_TryTurningTalent(oCreature)) return; + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, 0, oTarget)) return; + if(ai_TryDivineShieldFeat(oCreature, nInMelee)) return; + if(ai_TryDivineMightFeat(oCreature, nInMelee)) return; + } + } + // SIMPLE+ - Offensive talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget = OBJECT_INVALID; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_cntrspell.nss b/_module/nss/ai_a_cntrspell.nss new file mode 100644 index 00000000..a1bc9ec0 --- /dev/null +++ b/_module/nss/ai_a_cntrspell.nss @@ -0,0 +1,69 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_cntrspell +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the combat mode counter spell. + OBJECT_SELF is the creature 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); + // We are not in melee combat then we don't attack. + int bAttack = nInMelee; + if(!bAttack) + { + // If there are no casters, i.e. CLERIC or MAGES in the battle then attack. + struct stClasses stClasses = ai_GetFactionsClasses(oCreature); + if(!stClasses.CLERICS && !stClasses.MAGES) bAttack = TRUE; + } + // If we are not attacking and using magic then setup for counter spelling. + if(!bAttack && !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) + { + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + if(AI_DEBUG) ai_Debug("ai_a_cntrspell", "30", " Counterspell Mode? " + + IntToString(GetActionMode(OBJECT_SELF, ACTION_MODE_COUNTERSPELL))); + if(!GetActionMode(oCreature, ACTION_MODE_COUNTERSPELL)) + { + object oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER); + // We can only counter spells from a hasted caster if we are hasted as well. + if(ai_GetHasEffectType(oTarget, EFFECT_TYPE_HASTE) && + !ai_GetHasEffectType(oCreature, EFFECT_TYPE_HASTE)) + { + // If we have haste then we should cast it. + if(GetHasSpell(SPELL_HASTE, oCreature)) + { + if(AI_DEBUG) ai_Debug("ai_a_cntrspell", "42", "Opponent is hasted! Casting Haste."); + ActionCastSpellAtObject(SPELL_HASTE, oCreature); + ai_SetLastAction(oCreature, SPELL_HASTE); + return; + } + // If not then we need to go into normal combat. + else + { + if(AI_DEBUG) ai_Debug("ai_cntrspell", "50", "Opponent is hasted! Using ranged AI."); + ExecuteScript("ai_a_ranged"); + return; + } + } + if(oTarget != OBJECT_INVALID) + { + // First a good tactic for counter spelling is to be invisible. + if(ai_TryToBecomeInvisible(oCreature)) return; + // If we have attempted to become invisible or are invisible then + // it is time to counter spell. + if(AI_DEBUG) ai_Debug("ai_a_cntrspell", "61", "Setting Counterspell mode!"); + ActionCounterSpell(oTarget); + return; + } + } + } + if(AI_DEBUG) ai_Debug("ai_a_cntrspell", "67", "Situation is not good for counterspelling! Using ranged AI."); + ExecuteScript("ai_a_ranged"); +} diff --git a/_module/nss/ai_a_default.nss b/_module/nss/ai_a_default.nss new file mode 100644 index 00000000..6491fdf6 --- /dev/null +++ b/_module/nss/ai_a_default.nss @@ -0,0 +1,84 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_no_modes +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to not use any combat modes during combat ai. + OBJECT_SELF is the creature running the ai. + Our actions. + 1 - Get nearest enemy. + 2 - Check for healing and curing first. + 3 - Check moral if wounded and this is a simple+ battle. + 4 - Check for a magical ranged attack if not in melee and a difficult+ battle. + 5 - Check for a buff or summons if this is a difficult+ battle. + 6 - Check for a Class ability and an offensive spell if this is a simple+ battle. + 7 - Check for a physical attack. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, 0, oTarget)) return; + if(ai_TryDivineShieldFeat(oCreature, nInMelee)) return; + if(ai_TryDivineMightFeat(oCreature, nInMelee)) return; + } + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + // *************************** 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(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/_module/nss/ai_a_defensive.nss b/_module/nss/ai_a_defensive.nss new file mode 100644 index 00000000..f19523c4 --- /dev/null +++ b/_module/nss/ai_a_defensive.nss @@ -0,0 +1,77 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_defensive +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates put in to a defensive mode to protect themselves. + OBJECT_SELF is the creature running the ai. + Our actions. + 1 - Get nearest enemy and the difficulty of the battle. + 2 - Check for healing potions if this is a simple+ battle. + 3 - Check moral if wounded and is a simple+ battle. + 4 - Check for a magical ranged attack if not in melee and a difficult+ battle. + 5 - Check for a buff if this is a difficult+ battle. + 6 - Check for defensive ability such as knockdown, expertise or parry. + 7 - If we can't fight defensive then flee. + 8 - If we are out of range with no ability then stand and watch. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + if(AI_DEBUG) ai_Debug("ai_a_defensive", "25", "oNearest Enemy: " + GetName(oNearestEnemy) + + " Distance to Nearest Enemy: " + FloatToString(GetDistanceToObject(oNearestEnemy), 0, 2)); + // ALWAYS - Check for healing and cure talents. + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // SIMPLE+ - Check for moral and get what spell power we should be using. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // DIFFICULT+ - Class talents, Offensive AOE's, Defensive talents, and Potion talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + //************************** SKILL FEATURES ************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + // ************************** CLASS FEATURES *************************** + if(ai_TryBardSongFeat(oCreature)) return; + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + if(ai_TrySummonFamiliarTalent(oCreature)) return; + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS **************** + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if (ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + int nRound = ai_GetCurrentRound(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound, oTarget)) return; + } + } + object oTarget; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + if(nInMelee > 0) + { + 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); + if(oTarget == OBJECT_INVALID) oTarget = oNearestEnemy; + // Use knockdown when appropriate and the target is not immune. + if(ai_TryKnockdownFeat(oCreature, oTarget)) return; + if (ai_TryParry (oCreature)) return; + // We have tried everything to protect ourselves so the only thing left + // to do is man up and attack! + ai_DoPhysicalAttackOnLowestCR(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + return; + } + //********************** PHYSICAL ATTACKS ******************************** + // Even in defensive mode we want to be in battle so go find someone! + ai_DoPhysicalAttackOnBest(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); +} diff --git a/_module/nss/ai_a_druid.nss b/_module/nss/ai_a_druid.nss new file mode 100644 index 00000000..53b9303b --- /dev/null +++ b/_module/nss/ai_a_druid.nss @@ -0,0 +1,86 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_druid +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Druid class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // ************************** CLASS FEATURES *************************** + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** 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(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)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_fighter.nss b/_module/nss/ai_a_fighter.nss new file mode 100644 index 00000000..a5f3720c --- /dev/null +++ b/_module/nss/ai_a_fighter.nss @@ -0,0 +1,82 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: 0i_a_fighter +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Fighter class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget = OBJECT_INVALID; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_flanker.nss b/_module/nss/ai_a_flanker.nss new file mode 100644 index 00000000..fb6845a5 --- /dev/null +++ b/_module/nss/ai_a_flanker.nss @@ -0,0 +1,117 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_flanker +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to flank the enemy and not charge into combat. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) 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) + { + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + oTarget = GetLocalObject(oCreature, AI_PC_LOCKED_TARGET); + // ************************** Melee feat attacks ************************* + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + // Lets get the nearest target that is attacking someone besides me. We want to flank! + if(oTarget == OBJECT_INVALID) + { + if(!nInMelee) oTarget = ai_GetFlankTarget(oCreature); + // If there are few enemies then we can safely move around. + else if(nInMelee < 3 || ai_CanIMoveInCombat(oCreature)) + { + 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); + } + // If there are no enemies being attacked then lets stay back. + if(oTarget == OBJECT_INVALID) + { + if(nInMelee) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + // Lets get the strongest melee opponent in melee with us. + object oTarget = ai_GetHighestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + } + // ************************** Ranged feat attacks ************************** + else if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + oTarget = ai_GetLowestCRTarget(oCreature); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + else + { + ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); + return; + } + } + } + if(oTarget != OBJECT_INVALID) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + // Are we too far from our master? + object oMaster = GetMaster(); + if(GetDistanceBetween(oMaster, oCreature) > AI_RANGE_LONG) + { + ActionMoveToObject(oMaster, TRUE, AI_RANGE_CLOSE); + return; + } + ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); +} diff --git a/_module/nss/ai_a_invisible.nss b/_module/nss/ai_a_invisible.nss new file mode 100644 index 00000000..9772b322 --- /dev/null +++ b/_module/nss/ai_a_invisible.nss @@ -0,0 +1,123 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_invisible +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to use when they are invisible. + OBJECT_SELF is the creature 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); + // Has our master told us to not use magic? + int bUseMagic = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_EASY) + { + // *************************** SPELL TALENTS *************************** + if(ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) return; + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + int nRound = ai_GetCurrentRound(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound, oTarget)) return; + // ************************** CLASS FEATURES *************************** + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + if(ai_TrySummonFamiliarTalent(oCreature)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** SPELL TALENTS *************************** + if(!ai_GetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING)) + { + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + 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; + // ************************** Melee feat attacks ************************* + // If we won't loose invisibility then ranged attacks are ok! + // ************************ RANGED ATTACKS ******************************* + if(GetHasSpellEffect(SPELL_IMPROVED_INVISIBILITY) || GetHasSpellEffect(SPELLABILITY_AS_IMPROVED_INVISIBLITY)) + { + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + } + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee)) return; + 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_GetLowestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + talent tUse = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_MELEE, 20, oCreature); + if(GetIsTalentValid(tUse)) + { + int nId = GetIdFromTalent(tUse); + if(nId == FEAT_POWER_ATTACK) { if(ai_TryPowerAttackFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_KNOCKDOWN) { if(ai_TryKnockdownFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_SMITE_EVIL) { if(ai_TrySmiteEvilFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_SMITE_GOOD) { if(ai_TrySmiteGoodFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_IMPROVED_POWER_ATTACK) { if(ai_TryImprovedPowerAttackFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_FLURRY_OF_BLOWS) { if(ai_TryFlurryOfBlowsFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_STUNNING_FIST) { if(ai_TryStunningFistFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_SAP) { if(ai_TrySapFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_DISARM) { if(ai_TryDisarmFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_KI_DAMAGE) { if(ai_TryKiDamageFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_CALLED_SHOT) { if(ai_TryCalledShotFeat(oCreature, oTarget)) return; } + } + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} + diff --git a/_module/nss/ai_a_monk.nss b/_module/nss/ai_a_monk.nss new file mode 100644 index 00000000..607ce4cd --- /dev/null +++ b/_module/nss/ai_a_monk.nss @@ -0,0 +1,82 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_monk +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Monk class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryWholenessOfBodyFeat(oCreature)) return; + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_no_cmb_mode.nss b/_module/nss/ai_a_no_cmb_mode.nss new file mode 100644 index 00000000..1ffedb4d --- /dev/null +++ b/_module/nss/ai_a_no_cmb_mode.nss @@ -0,0 +1,131 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_o_cmb_modes +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to not use any combat modes during combat ai. + OBJECT_SELF is the creature running the ai. + Our actions. + 1 - Get nearest enemy. + 2 - Check for healing and curing first. + 3 - Check moral if wounded and this is a simple+ battle. + 4 - Check for a magical ranged attack if not in melee and a difficult+ battle. + 5 - Check for a buff or summons if this is a difficult+ battle. + 6 - Check for a Class ability and an offensive spell if this is a simple+ battle. + 7 - Check for a physical attack. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, 0, oTarget)) return; + 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) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + if(AI_DEBUG) ai_Debug("ai_a_no_modes", "78", "Check for ranged attack on weakest enemy!"); + object oTarget; + int bAlwaysAtk = !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK); + // ************************** Ranged feat attacks ************************** + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + !ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && + ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + // Lets pick off the weaker targets. + if(!nInMelee) + { + 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); + } + 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) + { + if(ai_TryRapidShotFeat(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; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + if(AI_DEBUG) ai_Debug("ai_a_no_modes", "117", "Check for melee attack on weakest enemy!"); + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee, bAlwaysAtk)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_PERCEPTION, bAlwaysAtk); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTargetForMeleeCombat(oCreature, nInMelee, bAlwaysAtk); + if(oTarget != OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("ai_a_no_modes", "126", GetName(OBJECT_SELF) + " does melee attack against weakest: " + GetName(oTarget) + "!"); + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} + diff --git a/_module/nss/ai_a_paladin.nss b/_module/nss/ai_a_paladin.nss new file mode 100644 index 00000000..24520a76 --- /dev/null +++ b/_module/nss/ai_a_paladin.nss @@ -0,0 +1,110 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_paladin +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Paladin class. + Paladins always protect their masters and face the strongest opponents first! + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell at! + // We are checking here since these opportunities are rare and we need + // to take advantage of them as often as possible. + 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; + } + if(!ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING)) + { + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + // Does our master want to be buffed first? + object oTarget = OBJECT_INVALID; + if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_MASTER)) oTarget = GetMaster(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, 0, oTarget)) return; + if(ai_TryDivineShieldFeat(oCreature, nInMelee)) return; + if(ai_TryDivineMightFeat(oCreature, nInMelee)) return; + } + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + if(ai_TryLayOnHands(oCreature)) return; + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget = OBJECT_INVALID; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Paladins ALWAYS protect their masters first! + oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + // Paladins ALWAYS protect their masters first! + oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) + { + int bCheckCombat = ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK); + if(bCheckCombat) oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee, FALSE); + // If always attacking Paladins ALWAYS attack the strongest opponent. + else oTarget = ai_GetHighestCRTargetForMeleeCombat(oCreature, nInMelee); + } + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_peaceful.nss b/_module/nss/ai_a_peaceful.nss new file mode 100644 index 00000000..b9bd3103 --- /dev/null +++ b/_module/nss/ai_a_peaceful.nss @@ -0,0 +1,81 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_a_peaceful +//////////////////////////////////////////////////////////////////////////////// + ai script mode for associates to use when they should remain out of combat. + OBJECT_SELF is the creature 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); + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + float fDistance = GetDistanceBetween(oCreature, oNearestEnemy); + // In Melee combat! + if(nInMelee > 0) + { + // If we are not being attacked then we should back out of combat. + if(ai_GetEnemyAttackingMe(oCreature) == OBJECT_INVALID) + { + if(AI_DEBUG) ai_Debug("ai_a_peaceful", "23", GetName(oCreature) + " is moving away from " + GetName(oNearestEnemy) + + "[" + FloatToString(AI_RANGE_MELEE - fDistance + 1.0, 0, 2) + "]" + " to use a ranged weapon."); + ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE); + // Lets move just out of melee range! + int bRun = ai_CanIMoveInCombat(oCreature); + ActionMoveAwayFromObject(oNearestEnemy, bRun, AI_RANGE_CLOSE + 2.0); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return; + } + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryImprovedExpertiseFeat(oCreature)) return; + if(ai_TryExpertiseFeat(oCreature)) return; + // Lets get the strongest melee opponent in melee with us. + object oTarget = ai_GetHighestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget == OBJECT_INVALID) oTarget = oNearestEnemy; + // Use knockdown when appropriate and the target is not immune. + if(ai_TryKnockdownFeat(oCreature, oTarget)) return; + if (ai_TryParry(oCreature)) return; + // We have tried everything to protect ourselves so the only thing left + // to do is man up and attack! + // Physical attacks are under TALENT_CATEGORY_HARMFUL_MELEE(22). + ai_DoPhysicalAttackOnNearest(oCreature, nInMelee); + return; + } + if(fDistance <= AI_RANGE_LONG) + { + if(AI_DEBUG) ai_Debug("ai_a_peaceful", "49", GetName(oCreature) + " is moving away from " + GetName(oNearestEnemy) + + "[" + FloatToString(AI_RANGE_LONG - fDistance, 0, 2) + "]" + "."); + ai_SetLastAction(oCreature, AI_LAST_ACTION_MOVE); + // Lets move out of close range! + ActionMoveAwayFromObject(oNearestEnemy, TRUE, AI_RANGE_LONG + 2.0); + ActionDoCommand(ExecuteScript("0e_do_combat_rnd", oCreature)); + return; + } + //************************* OUT OF COMBAT ************************** + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, 0, oCreature)) return; + if(ai_TryCureConditionTalent(oCreature, 0)) return; + //************************** DEFENSIVE TALENTS *************************** + // Has our master told us to not use magic? + int bUseMagic = !ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC); + if(bUseMagic) + { + // If can turn invisible then we should probably do that! + if(ai_UseTalent(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY_SPHERE, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_SANCTUARY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_ETHEREALNESS, oCreature)) return; // Greater Sanctuary + if(ai_UseTalent(oCreature, SPELLABILITY_AS_IMPROVED_INVISIBLITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELLABILITY_AS_INVISIBILITY, oCreature)) return; + int nMaxLevel = ai_GetMonsterTalentMaxLevel(oCreature); + // Summons are powerfull and should be used as much as possible. + if(ai_UseCreatureTalent(oCreature, AI_TALENT_SUMMON, nInMelee, nMaxLevel)) return; + if(ai_UseCreatureTalent(oCreature, AI_TALENT_PROTECTION, nInMelee, nMaxLevel)) return; + } + // Stand and watch the battle we don't want to provoke anyone! + if(AI_DEBUG) ai_Debug("ai_a_peaceful", "80", GetName(oCreature) + " is holding here."); +} diff --git a/_module/nss/ai_a_polymorphed.nss b/_module/nss/ai_a_polymorphed.nss new file mode 100644 index 00000000..107e6e92 --- /dev/null +++ b/_module/nss/ai_a_polymorphed.nss @@ -0,0 +1,70 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_polymorphed +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for polymorphed associates. + We check for abilities based on the form we are using and if we should polymorph back. + OBJECT_SELF is the creature running the ai. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +void ai_DoActions(object oCreature, int nForm) +{ + 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!"); + ai_RemoveASpecificEffect(oCreature, EFFECT_TYPE_POLYMORPH); + return; + } + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // When polymorphed we turn back then check moral. + //if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget = ai_GetLowestCRTargetForMeleeCombat(oCreature, nInMelee); + // If we don't find a target then we don't want to fight anyone! + 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/_module/nss/ai_a_ranged.nss b/_module/nss/ai_a_ranged.nss new file mode 100644 index 00000000..477937de --- /dev/null +++ b/_module/nss/ai_a_ranged.nss @@ -0,0 +1,129 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_ranged +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates to use the ranged ai. + OBJECT_SELF is the creature running the ai. + Will attempt to use ranged weapons until surrounded. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + //************************** SKILL FEATURES ************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + // ************************** CLASS FEATURES *************************** + // Turning is basically a powerful AOE so treat it like one. + if(ai_TryTurningTalent(oCreature)) return; + if(ai_TryBarbarianRageFeat(oCreature)) return; + if(ai_TryBardSongFeat(oCreature)) return; + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + if(ai_TrySummonFamiliarTalent(oCreature)) return; + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED)) + { + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + nInMelee < 3) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Lets defend master, nearest favored enemy, ranged, sneak, weakest targets. + if(!nInMelee) + { + 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_GetRangedTarget(oCreature); + if(oTarget == OBJECT_INVALID && ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(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) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + } + // ************************** Melee feat attacks ************************* + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + if(nInMelee) + { + oTarget = ai_GetEnemyAttackingMe(oCreature); + if(oTarget != OBJECT_INVALID) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + 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); + 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; + } + } + } + if(oNearestEnemy != OBJECT_INVALID) + { + float fDistance = GetDistanceBetween(oCreature, oNearestEnemy); + float fRange = AI_RANGE_LONG; + if(GetIsAreaInterior(GetArea(oCreature))) fRange = AI_RANGE_CLOSE; + if(GetHasFeat(FEAT_SNEAK_ATTACK, oCreature)) fRange = AI_RANGE_CLOSE; + if(fDistance < fRange) + { + int bRun = ai_CanIMoveInCombat(oCreature); + ActionMoveAwayFromObject(oNearestEnemy, bRun, fRange - fDistance + 2.0); + } + } + else ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); +} + diff --git a/_module/nss/ai_a_ranger.nss b/_module/nss/ai_a_ranger.nss new file mode 100644 index 00000000..179298f0 --- /dev/null +++ b/_module/nss/ai_a_ranger.nss @@ -0,0 +1,96 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_ranger +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Ranger class. + Rangers will take out favored enemies first! + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + //************************** SKILL FEATURES ************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + // ************************** CLASS FEATURES *************************** + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) + { + // Lets pick off the weakest targets. + if(!nInMelee) + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature); + } + else + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTarget(oCreature, AI_RANGE_MELEE); + } + } + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + // Our master may have setup to check difficulty before we move into melee. + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_PERCEPTION, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_rogue.nss b/_module/nss/ai_a_rogue.nss new file mode 100644 index 00000000..10e1ae05 --- /dev/null +++ b/_module/nss/ai_a_rogue.nss @@ -0,0 +1,83 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_rogue +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Rogue class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // *************************** 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) + { + 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(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_sorcerer.nss b/_module/nss/ai_a_sorcerer.nss new file mode 100644 index 00000000..5e4a3b74 --- /dev/null +++ b/_module/nss/ai_a_sorcerer.nss @@ -0,0 +1,75 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_sorcerer +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Sorcerer class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_a_taunter.nss b/_module/nss/ai_a_taunter.nss new file mode 100644 index 00000000..b06fed80 --- /dev/null +++ b/_module/nss/ai_a_taunter.nss @@ -0,0 +1,53 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_a_taunter +//////////////////////////////////////////////////////////////////////////////// + ai script for creatures using defined to use the taunt skill. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + //************************** SKILL FEATURES ************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + // ************************** CLASS FEATURES *************************** + if(ai_TryBarbarianRageFeat(oCreature)) return; + if(ai_TryBardSongFeat(oCreature)) return; + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Class and Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + // ************************** CLASS FEATURES *************************** + if(ai_TryTurningTalent(oCreature)) return; + // *************************** 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; + } + } + // Taunt the nearest target! + if (ai_TryTaunt (oCreature, ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee))) return; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + ai_DoPhysicalAttackOnLowestCR(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); +} diff --git a/_module/nss/ai_a_wizard.nss b/_module/nss/ai_a_wizard.nss new file mode 100644 index 00000000..dfad8c30 --- /dev/null +++ b/_module/nss/ai_a_wizard.nss @@ -0,0 +1,77 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_a_wizard +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for associates using the Wizard class. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + int nDifficulty = ai_GetDifficulty(oCreature); + int nMaxLevel; + // Check for moral and get the maximum spell level we should use. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + if(nInMelee && ai_MoralCheck(oCreature)) return; + nMaxLevel = ai_GetAssociateTalentMaxLevel(oCreature, nDifficulty); + } + // Skill, Class, Offensive AOE's, and Defensive talents. + if(nDifficulty >= AI_COMBAT_MODERATE) + { + // ************************** CLASS FEATURES *************************** + if(ai_TrySummonFamiliarTalent(oCreature)) return; + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + } + // Offensive single target talents. + if(nDifficulty >= AI_COMBAT_EFFORTLESS) + { + 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; + } + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Are we suppose to protect our master first? + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + 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(oTarget != OBJECT_INVALID) + { + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER)) oTarget = ai_GetLowestCRAttackerOnMaster(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetBestTargetForMeleeCombat(oCreature, nInMelee, !ai_GetAIMode(oCreature, AI_MODE_CHECK_ATTACK)); + if(oTarget != OBJECT_INVALID) ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + else ai_SearchForHiddenCreature(oCreature, FALSE); +} diff --git a/_module/nss/ai_ambusher.nss b/_module/nss/ai_ambusher.nss new file mode 100644 index 00000000..829a3dae --- /dev/null +++ b/_module/nss/ai_ambusher.nss @@ -0,0 +1,100 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_ambusher +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for ambushing creatures (Any). + OBJECT_SELF is the creature 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); + // Rule used to disable ambush if the player wants to. + if(!GetLocalInt(GetModule(), AI_RULE_AMBUSH)) + { + ExecuteScript("ai_default", oCreature); + return; + } + // If can turn invisible then we should probably do that! + if(ai_UseTalent(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY_SPHERE, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_SANCTUARY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_ETHEREALNESS, oCreature)) return; // Greater Sanctuary + if(ai_UseTalent(oCreature, SPELLABILITY_AS_IMPROVED_INVISIBLITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELLABILITY_AS_INVISIBILITY, oCreature)) return; + // Check the battle field to see if anyone see us? + int nEnemyIndex = ai_GetNearestIndexThatSeesUs(oCreature); + // If seen, can we try to hide now? + if(nEnemyIndex) + { + // Check for an attacker and can they see through invisibility? + object oAttacker = ai_GetEnemyAttackingMe(oCreature); + int bCanSeeInvisible; + if(oAttacker != OBJECT_INVALID) + { + bCanSeeInvisible = ai_GetHasEffectType(oAttacker, EFFECT_TYPE_SEEINVISIBLE); + if(!bCanSeeInvisible) bCanSeeInvisible = ai_GetHasEffectType(oAttacker, EFFECT_TYPE_TRUESEEING); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_5_FEET, oCreature); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_10_FEET, oCreature); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_60_FEET, oCreature); + } + if(AI_DEBUG) ai_Debug("ai_ambusher", "43", "bCanSeeInvisible: " + IntToString(bCanSeeInvisible)); + if(!bCanSeeInvisible) + { + if(GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT, oCreature)) + { + if(!GetActionMode(oCreature, ACTION_MODE_STEALTH)) + { + if(AI_DEBUG) ai_Debug("ai_ambusher", "50", GetName(oCreature) + " is using hide in plain sight!"); + ClearAllActions(TRUE); + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + return; + } + } + // Does not have hide in plain sight. + else + { + string sEnemyIndex = IntToString(nEnemyIndex); + float fEnemyDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sEnemyIndex); + if(AI_DEBUG) ai_Debug("ai_ambusher", "61", "fDistance: " + FloatToString(fEnemyDistance, 0, 2)); + if(fEnemyDistance >= AI_RANGE_LONG) + { + int bTried = GetLocalInt(oCreature, AI_TRIED_TO_HIDE); + if(!bTried) + { + // Move away so we can hide. + if(AI_DEBUG) ai_Debug("ai_ambusher", "68", GetName(oCreature) + " is trying to move away to hide!"); + SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + object oEnemy = GetLocalObject(oCreature, AI_ENEMY + sEnemyIndex); + ActionMoveAwayFromObject(oEnemy, TRUE, AI_RANGE_BATTLEFIELD); + SetLocalInt(oCreature, AI_TRIED_TO_HIDE, 3); + return; + } + else SetLocalInt(oCreature, AI_TRIED_TO_HIDE, GetLocalInt(oCreature, AI_TRIED_TO_HIDE) - 1); + } + // We have been seen by an enemy too close to us so drop stealth. + else SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + } + } + // The enemy can see through stealth so lets drop it. + else SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + } + // We are not in stealth mode so lets get there. + else if(!GetActionMode(oCreature, ACTION_MODE_STEALTH)) + { + // Use any hiding talents we have + if(AI_DEBUG) ai_Debug("ai_ambusher", "88", GetName(oCreature) + " is trying to hide!"); + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + SetLocalInt(oCreature, AI_TRIED_TO_HIDE, 3); + return; + } + // If we have givin up on stealth do our normal actions. + string sScript = GetLocalString(oCreature, AI_DEFAULT_SCRIPT); + if(sScript == "ai_ambusher" || sScript == "") sScript = "ai_default"; + if(AI_DEBUG) ai_Debug("ai_ambusher", "96", "sScript: " + sScript + " AI_DEFAULT_SCRIPT: " + GetLocalString(oCreature, AI_DEFAULT_SCRIPT)); + ExecuteScript(sScript, oCreature); +} diff --git a/_module/nss/ai_barbarian.nss b/_module/nss/ai_barbarian.nss new file mode 100644 index 00000000..69efcb42 --- /dev/null +++ b/_module/nss/ai_barbarian.nss @@ -0,0 +1,71 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_barbarian +//////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Barbarian. + OBJECT_SELF is the creature 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); + object oTarget; + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature)) + { + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** CLASS FEATURES **************************** + if(ai_TryBarbarianRageFeat(oCreature)) return; + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // *************************** RANGED ATTACKS ***************************** + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(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 *************************** + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_bard.nss b/_module/nss/ai_bard.nss new file mode 100644 index 00000000..9dde198f --- /dev/null +++ b/_module/nss/ai_bard.nss @@ -0,0 +1,67 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_bard +//////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Bard. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** CLASS FEATURES **************************** + if(ai_TryBardSongFeat(oCreature)) return; + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_cleric.nss b/_module/nss/ai_cleric.nss new file mode 100644 index 00000000..34bd1e67 --- /dev/null +++ b/_module/nss/ai_cleric.nss @@ -0,0 +1,68 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_cleric +//////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Cleric. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** CLASS FEATURES **************************** + if(ai_TryTurningTalent(oCreature)) return; + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; + if(ai_TryDivineShieldFeat(oCreature, nInMelee)) return; + if(ai_TryDivineMightFeat(oCreature, nInMelee)) 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + 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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_cntrspell.nss b/_module/nss/ai_cntrspell.nss new file mode 100644 index 00000000..04939c9f --- /dev/null +++ b/_module/nss/ai_cntrspell.nss @@ -0,0 +1,68 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_cntrspell +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the combat mode counter spell. + OBJECT_SELF is the creature 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); + // We are not in melee combat then we don't attack. + int bAttack = nInMelee; + if(!bAttack) + { + // If there are no casters, i.e. CLERIC or MAGES in the battle then attack. + struct stClasses stClasses = ai_GetFactionsClasses(oCreature); + if(!stClasses.CLERICS && !stClasses.MAGES) bAttack = TRUE; + } + // If we are not attacking then setup for counter spelling. + if(!bAttack) + { + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(AI_DEBUG) ai_Debug("ai_cntrspell", "29", " Counterspell Mode? " + + IntToString(GetActionMode(OBJECT_SELF, ACTION_MODE_COUNTERSPELL))); + if(!GetActionMode(oCreature, ACTION_MODE_COUNTERSPELL)) + { + object oTarget = ai_GetNearestClassTarget(oCreature, AI_CLASS_TYPE_CASTER); + // We can only counter spells from a hasted caster if we are hasted as well. + if(ai_GetHasEffectType(oTarget, EFFECT_TYPE_HASTE) && + !ai_GetHasEffectType(oCreature, EFFECT_TYPE_HASTE)) + { + // If we have haste then we should cast it. + if(GetHasSpell(SPELL_HASTE, oCreature)) + { + if(AI_DEBUG) ai_Debug("ai_cntrspell", "41", "Opponent is hasted! Casting Haste."); + ActionCastSpellAtObject(SPELL_HASTE, oCreature); + ai_SetLastAction(oCreature, SPELL_HASTE); + return; + } + // If not then we need to go into normal combat. + else + { + if(AI_DEBUG) ai_Debug("ai_cntrspell", "49", "Opponent is hasted! Using ranged AI."); + ExecuteScript("ai_ranged"); + return; + } + } + if(oTarget != OBJECT_INVALID) + { + // First a good tactic for counter spelling is to be invisible. + if(ai_TryToBecomeInvisible(oCreature)) return; + // If we have attempted to become invisible or are invisible then + // it is time to counter spell. + if(AI_DEBUG) ai_Debug("ai_cntrspell", "60", "Setting Counterspell mode!"); + ActionCounterSpell(oTarget); + return; + } + } + } + if(AI_DEBUG) ai_Debug("ai_cntrspell", "66", "Situation is not good for counterspelling! Using ranged AI."); + ExecuteScript("ai_ranged"); +} diff --git a/_module/nss/ai_coward.nss b/_module/nss/ai_coward.nss new file mode 100644 index 00000000..b88e4243 --- /dev/null +++ b/_module/nss/ai_coward.nss @@ -0,0 +1,133 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_coward +//////////////////////////////////////////////////////////////////////////////// + ai script for cowardly creatures (Any) used when they fail a moral check or + when associates are to remain out of combat. + OBJECT_SELF is the creature 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 us. + int nInMelee = ai_GetNumOfEnemiesInRange(oCreature); + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + // If we have been healed up then get back in there! + if(ai_GetPercHPLoss(oCreature) > AI_HEALTH_WOUNDED) + { + string sDefaultCombatScript = GetLocalString(oCreature, AI_DEFAULT_SCRIPT); + SetLocalString(oCreature, AI_COMBAT_SCRIPT, sDefaultCombatScript); + ExecuteScript(sDefaultCombatScript, oCreature); + return; + } + // In Melee combat! + if(nInMelee) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryImprovedExpertiseFeat(oCreature)) return; + if(ai_TryExpertiseFeat(oCreature)) return; + // Lets get the strongest melee opponent in melee with us. + object oTarget = ai_GetHighestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget == OBJECT_INVALID) oTarget = oNearestEnemy; + // Use knockdown when appropriate and the target is not immune. + if(ai_TryKnockdownFeat(oCreature, oTarget)) return; + if (ai_TryParry(oCreature)) return; + // We have tried everything to protect ourselves so the only thing left + // to do is man up and attack! + // Physical attacks are under TALENT_CATEGORY_HARMFUL_MELEE(22). + ai_DoPhysicalAttackOnNearest(oCreature, nInMelee); + return; + } + else + { + // If can turn invisible then we should probably do that! + if(ai_UseTalent(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_INVISIBILITY_SPHERE, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_SANCTUARY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELL_ETHEREALNESS, oCreature)) return; // Greater Sanctuary + if(ai_UseTalent(oCreature, SPELLABILITY_AS_IMPROVED_INVISIBLITY, oCreature)) return; + if(ai_UseTalent(oCreature, SPELLABILITY_AS_INVISIBILITY, oCreature)) return; + // If we are seen by the enemy we need to move back so we can hide. + int nEnemyIndex = ai_GetNearestIndexThatSeesUs(oCreature); + if(nEnemyIndex) + { + // Check for an attacker and can they see through invisibility? + object oAttacker = ai_GetEnemyAttackingMe(oCreature); + int bCanSeeInvisible; + if(oAttacker != OBJECT_INVALID) + { + bCanSeeInvisible = ai_GetHasEffectType(oAttacker, EFFECT_TYPE_SEEINVISIBLE); + if(!bCanSeeInvisible) bCanSeeInvisible = ai_GetHasEffectType(oAttacker, EFFECT_TYPE_TRUESEEING); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_5_FEET, oCreature); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_10_FEET, oCreature); + if(!bCanSeeInvisible) bCanSeeInvisible = GetHasFeat(FEAT_BLINDSIGHT_60_FEET, oCreature); + } + if(!bCanSeeInvisible) + { + if(GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT, oCreature)) + { + if(!GetActionMode(oCreature, ACTION_MODE_STEALTH)) + { + if(AI_DEBUG) ai_Debug("ai_coward", "74", GetName(oCreature) + " is using hide in plain sight!"); + ClearAllActions(TRUE); + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + return; + } + } + // Does not have hide in plain sight. + else + { + string sEnemyIndex = IntToString(nEnemyIndex); + float fEnemyDistance = GetLocalFloat(oCreature, AI_ENEMY_RANGE + sEnemyIndex); + if(AI_DEBUG) ai_Debug("ai_coward", "85", "fDistance: " + FloatToString(fEnemyDistance, 0, 2)); + if(fEnemyDistance >= AI_RANGE_CLOSE) + { + int bTried = GetLocalInt(oCreature, AI_TRIED_TO_HIDE); + if(!bTried) + { + // Move away so we can hide. + if(AI_DEBUG) ai_Debug("ai_coward", "93", GetName(oCreature) + " is trying to move away to hide!"); + SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + object oEnemy = GetLocalObject(oCreature, AI_ENEMY + sEnemyIndex); + ActionMoveAwayFromObject(oEnemy, TRUE, AI_RANGE_BATTLEFIELD); + SetLocalInt(oCreature, AI_TRIED_TO_HIDE, 3); + return; + } + else SetLocalInt(oCreature, AI_TRIED_TO_HIDE, GetLocalInt(oCreature, AI_TRIED_TO_HIDE) - 1); + } + // We have been seen by an enemy near us so drop stealth. + else SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + } + } + // The enemy can see through stealth so lets drop it. + else SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + } + // We are not in stealth mode so lets get there. + else if(!GetActionMode(oCreature, ACTION_MODE_STEALTH)) + { + // Use any hiding talents we have + if(AI_DEBUG) ai_Debug("ai_coward", "113", GetName(oCreature) + " is trying to hide!"); + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + SetLocalInt(oCreature, AI_TRIED_TO_HIDE, 3); + return; + } + } + // Either we cannot go into stealth or we are in stealth so do something else. + //************************* OUT OF MELEE COMBAT ************************** + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, 0, oCreature)) return; + if(ai_TryCureConditionTalent(oCreature, 0)) return; + int nMaxLevel = ai_GetMonsterTalentMaxLevel(oCreature); + //************************** DEFENSIVE TALENTS *************************** + if(GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS)) + { + if(ai_TrySummonFamiliarTalent(oCreature)) return; + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + } + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel)) return; + // Stand and watch the battle we don't want to provoke anyone! + if(AI_DEBUG) ai_Debug("ai_coward", "132", GetName(oCreature) + " is holding here."); +} diff --git a/_module/nss/ai_default.nss b/_module/nss/ai_default.nss new file mode 100644 index 00000000..f84f6f7d --- /dev/null +++ b/_module/nss/ai_default.nss @@ -0,0 +1,50 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_default +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for default creatures(Any). + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** SKILL FEATURES **************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + //**************************** CLASS FEATURES **************************** + if(GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS)) + { + if(ai_TrySummonFamiliarTalent(oCreature)) return; + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + } + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; + if(ai_TryDivineShieldFeat(oCreature, nInMelee)) return; + if(ai_TryDivineMightFeat(oCreature, nInMelee)) 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; + //**************************** 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/_module/nss/ai_defensive.nss b/_module/nss/ai_defensive.nss new file mode 100644 index 00000000..15f52751 --- /dev/null +++ b/_module/nss/ai_defensive.nss @@ -0,0 +1,48 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_defensive +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures put in to a defensive mode to protect themselves(Any). + OBJECT_SELF is the creature 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); + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) return; + int nMaxLevel = ai_GetMonsterTalentMaxLevel(oCreature); + //**************************** SKILL FEATURES **************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + //**************************** CLASS FEATURES **************************** + 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; + } + //************************** DEFENSIVE TALENTS *************************** + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel)) return; + //******************** DEFENSIVE MELEE FEATS ***************************** + if(nInMelee > 0) + { + if(ai_TryImprovedExpertiseFeat(oCreature)) return; + if(ai_TryExpertiseFeat(oCreature)) return; + // Lets get the strongest melee opponent in melee with us. + object oTarget = ai_GetHighestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget == OBJECT_INVALID) oTarget = oNearestEnemy; + // Use knockdown when appropriate and the target is not immune + if(ai_TryKnockdownFeat(oCreature, oTarget)) return; + if(ai_TryParry(oCreature)) return; + } + //********************** PHYSICAL ATTACKS ******************************** + // Even in defensive mode we want to be in battle so go find someone! + ai_DoPhysicalAttackOnNearest(oCreature, nInMelee); +} diff --git a/_module/nss/ai_dragon.nss b/_module/nss/ai_dragon.nss new file mode 100644 index 00000000..a82362ce --- /dev/null +++ b/_module/nss/ai_dragon.nss @@ -0,0 +1,51 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_dragon +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for dragons. + OBJECT_SELF is the dragons 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + // Dragons do not flee! if(nInMelee && ai_MoralCheck(oCreature)) 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 *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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 ******************************** + object oTarget = ai_GetLowestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(GetDistanceBetween(oCreature, oTarget) > AI_RANGE_CLOSE) + { + // Can we do a crush attack(HD 18+)? + if(ai_TryCrushAttack(oCreature, oTarget)) return; + ai_FlyToTarget(oCreature, oTarget); + return; + } + 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/_module/nss/ai_druid.nss b/_module/nss/ai_druid.nss new file mode 100644 index 00000000..7ec19fa4 --- /dev/null +++ b/_module/nss/ai_druid.nss @@ -0,0 +1,70 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_druid +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Druid. + OBJECT_SELF is the creature 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); + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** 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; + //********************** 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; + //**************************** 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 ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + 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(oTarget != OBJECT_INVALID) + { + 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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_fighter.nss b/_module/nss/ai_fighter.nss new file mode 100644 index 00000000..04f4ce06 --- /dev/null +++ b/_module/nss/ai_fighter.nss @@ -0,0 +1,65 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_fighter +//////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Fighter. + OBJECT_SELF is the creature 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); + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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 ***************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // *************************** RANGED ATTACKS **************************** + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(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 **************************** + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryWhirlwindFeat (oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee); + if (oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents (oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_flanker.nss b/_module/nss/ai_flanker.nss new file mode 100644 index 00000000..20a65389 --- /dev/null +++ b/_module/nss/ai_flanker.nss @@ -0,0 +1,102 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_flanker +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for monsters to flank the enemy and not charge into combat. + OBJECT_SELF is the creature 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); + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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 ***************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + //**************************** 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; + } + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Melee feat attacks ************************* + // Lets get the nearest target that is attacking someone besides me. We want to flank! + if(oTarget == OBJECT_INVALID) + { + if(!nInMelee) oTarget = ai_GetFlankTarget(oCreature); + // If there are few enemies then we can safely move around. + else if(nInMelee < 3 || ai_CanIMoveInCombat(oCreature)) + { + 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); + } + // If there are no enemies being attacked then lets stay back. + if(oTarget == OBJECT_INVALID) + { + if(nInMelee) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + // Lets get the strongest melee opponent in melee with us. + object oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + } + // ************************** Ranged feat attacks ************************** + if(!ai_GetAIMode(oCreature, AI_MODE_STOP_RANGED) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(ai_TryRangedSneakAttack(oCreature, nInMelee)) return; + oTarget = ai_GetNearestTarget(oCreature); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + else + { + ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); + return; + } + } + } + if(oTarget != OBJECT_INVALID) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + return; + } + ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); +} diff --git a/_module/nss/ai_incorporeal.nss b/_module/nss/ai_incorporeal.nss new file mode 100644 index 00000000..cdd20bdd --- /dev/null +++ b/_module/nss/ai_incorporeal.nss @@ -0,0 +1,83 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_incorporeal +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures that are incorporeal. + oCreature is the creature 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); + if (nInMelee && ai_MoralCheck (oCreature)) 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; + //**************************** 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(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + if(ai_TrySummonFamiliarTalent(oCreature)) return; + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if (!GetHasFeatEffect (FEAT_BARBARIAN_RAGE, oCreature) && ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if (ai_TryRangedSneakAttack (oCreature, nInMelee)) return; + string sIndex; + if (!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget (oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat (oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE); + return; + } + } + if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************* MELEE ATTACKS ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee); + 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_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); + } + } + else ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_invisible.nss b/_module/nss/ai_invisible.nss new file mode 100644 index 00000000..7d42b21b --- /dev/null +++ b/_module/nss/ai_invisible.nss @@ -0,0 +1,93 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_invisible +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures(Any) that are invisible. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) return; + int nMaxLevel = ai_GetMonsterTalentMaxLevel(oCreature); + // Skill, Class, Offensive AOE's, and Defensive talents. + // *************************** SPELL TALENTS *************************** + // ********** PROTECTION/ENHANCEMENT/SUMMON TALENTS ************ + int nRound = ai_GetCurrentRound(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; + // ************************** CLASS FEATURES *************************** + if(GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS)) + { + if(ai_TrySummonFamiliarTalent(oCreature)) return; + if(ai_TrySummonAnimalCompanionTalent(oCreature)) return; + } + // ******************* OFFENSIVE AOE TALENTS *********************** + // Check the battlefield for a group of enemies to shoot a big spell 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; + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // If we won't loose invisibility then ranged attacks are ok! + // ************************ RANGED ATTACKS ******************************* + if(GetHasSpellEffect(SPELL_IMPROVED_INVISIBILITY) || GetHasSpellEffect(SPELLABILITY_AS_IMPROVED_INVISIBLITY)) + { + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(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 feat attacks ************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee)) return; + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetLowestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + talent tUse = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_MELEE, 20, oCreature); + if(GetIsTalentValid(tUse)) + { + int nId = GetIdFromTalent(tUse); + if(nId == FEAT_POWER_ATTACK) { if(ai_TryPowerAttackFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_KNOCKDOWN) { if(ai_TryKnockdownFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_SMITE_EVIL) { if(ai_TrySmiteEvilFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_SMITE_GOOD) { if(ai_TrySmiteGoodFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_IMPROVED_POWER_ATTACK) { if(ai_TryImprovedPowerAttackFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_FLURRY_OF_BLOWS) { if(ai_TryFlurryOfBlowsFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_STUNNING_FIST) { if(ai_TryStunningFistFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_SAP) { if(ai_TrySapFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_DISARM) { if(ai_TryDisarmFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_KI_DAMAGE) { if(ai_TryKiDamageFeat(oCreature, oTarget)) return; } + else if(nId == FEAT_CALLED_SHOT) { if(ai_TryCalledShotFeat(oCreature, oTarget)) return; } + } + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_monk.nss b/_module/nss/ai_monk.nss new file mode 100644 index 00000000..d6ff1b65 --- /dev/null +++ b/_module/nss/ai_monk.nss @@ -0,0 +1,65 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_monk +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Monk. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if (ai_TryWholenessOfBodyFeat (oCreature)) return; + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck (oCreature)) 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 *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if (!nInMelee) oTarget = ai_GetNearestTarget (oCreature); + else oTarget = ai_GetNearestTarget (oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + 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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee); + if (oTarget != OBJECT_INVALID) + { + if (ai_TryMeleeTalents (oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_paladin.nss b/_module/nss/ai_paladin.nss new file mode 100644 index 00000000..d45d2231 --- /dev/null +++ b/_module/nss/ai_paladin.nss @@ -0,0 +1,71 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_paladin +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Paladin. + Paladins face the strongest opponents on the battlefield first! + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** CLASS FEATURES **************************** + if(ai_TryTurningTalent(oCreature)) return; + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; + if(ai_TryDivineShieldFeat(oCreature, nInMelee)) return; + if(ai_TryDivineMightFeat(oCreature, nInMelee)) 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + 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(oTarget != OBJECT_INVALID) + { + 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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + // Paladins face the biggest challenges first! + oTarget = ai_GetHighestCRTargetForMeleeCombat(oCreature, nInMelee); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryMeleeTalents(oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_polymorphed.nss b/_module/nss/ai_polymorphed.nss new file mode 100644 index 00000000..9a7630a6 --- /dev/null +++ b/_module/nss/ai_polymorphed.nss @@ -0,0 +1,55 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_polymorphed +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for polymorphed creatures. + We check for abilities based on the form we are using and if we should polymorph back. + OBJECT_SELF is the creature running the ai. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// Programmer: Philos +////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" +void ai_DoActions(object oCreature, int nForm) +{ + 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!"); + ai_RemoveASpecificEffect(oCreature, EFFECT_TYPE_POLYMORPH); + return; + } + int nMaxLevel = ai_GetMonsterTalentMaxLevel(oCreature); + // When polymorphed we turn back then check moral. + // if(nInMelee && ai_MoralCheck(oCreature)) return; + // Skill, Class, Offensive AOE's, and Defensive talents. + // *************************** SPELL TALENTS *************************** + if(ai_CheckForAssociateSpellTalent(oCreature, nInMelee, nMaxLevel)) return; + // Class and Offensive single target talents. + // *************************** SPELL TALENTS *************************** + 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! + 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/_module/nss/ai_ranged.nss b/_module/nss/ai_ranged.nss new file mode 100644 index 00000000..3a46228c --- /dev/null +++ b/_module/nss/ai_ranged.nss @@ -0,0 +1,116 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_ranged +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for monsters to use the ranged ai. + OBJECT_SELF is the creature running the ai. + Will attempt to use ranged weapons/spells until surrounded. +*///////////////////////////////////////////////////////////////////////////////////////////////////// +// 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + // Check for moral and get the maximum spell level we should use. + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** 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; + } + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + if(ai_TryDefensiveTalents(oCreature, nInMelee, nMaxLevel, nRound)) return; + // ************************** CLASS FEATURES ******************************* + if(ai_TryTurningTalent(oCreature)) 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + object oTarget; + // ************************** Ranged feat attacks ************************** + if(!GetHasFeatEffect(FEAT_BARBARIAN_RAGE, oCreature) && + (nInMelee < 3 || ai_GetEnemyAttackingMe(oCreature) == OBJECT_INVALID)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + // Lets pick off the ranged then nearest targets. + if(!nInMelee) + { + 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); + } + 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) + { + if(ai_TryRapidShotFeat(oCreature, oTarget, nInMelee)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************** Melee feat attacks ************************* + object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); + if(nInMelee) + { + oTarget = ai_GetEnemyAttackingMe(oCreature); + if(oTarget != OBJECT_INVALID) + { + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if(ai_TrySneakAttack(oCreature, nInMelee)) return; + if(ai_TryWhirlwindFeat(oCreature)) return; + if(oTarget == OBJECT_INVALID) 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; + } + } + } + if(oNearestEnemy != OBJECT_INVALID) + { + float fDistance = GetDistanceBetween(oCreature, oNearestEnemy); + float fRange = AI_RANGE_LONG; + if(GetHasFeat(FEAT_SNEAK_ATTACK, oCreature)) fRange = AI_RANGE_CLOSE; + if(fDistance < fRange) + { + int bRun = ai_CanIMoveInCombat(oCreature); + ActionMoveAwayFromObject(oNearestEnemy, bRun, fRange - fDistance + 2.0); + } + } + else ai_SearchForHiddenCreature(oCreature, FALSE, OBJECT_INVALID, AI_RANGE_CLOSE); +} + diff --git a/_module/nss/ai_ranger.nss b/_module/nss/ai_ranger.nss new file mode 100644 index 00000000..5878cfb5 --- /dev/null +++ b/_module/nss/ai_ranger.nss @@ -0,0 +1,79 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_ranger +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Ranger. + Need to add ---> Rangers will take out favored enemies first! + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + //**************************** SKILL FEATURES **************************** + if(ai_TryAnimalEmpathy(oCreature)) return; + //**************************** CLASS FEATURES **************************** + if(GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS) && ai_TrySummonAnimalCompanionTalent(oCreature)) return; + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature); + } + else + { + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); + if(oTarget == OBJECT_INVALID) oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + } + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestFavoredEnemyTarget(oCreature, AI_RANGE_MELEE); + 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); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_rogue.nss b/_module/nss/ai_rogue.nss new file mode 100644 index 00000000..81c4500b --- /dev/null +++ b/_module/nss/ai_rogue.nss @@ -0,0 +1,66 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_rogue +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Rogue. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck (oCreature)) 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 *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if (ai_TryRangedSneakAttack (oCreature, nInMelee)) return; + oTarget = ai_GetNearestTarget (oCreature); + if(oTarget != OBJECT_INVALID) + { + if (ai_TryRapidShotFeat (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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + if (ai_TrySneakAttack (oCreature, nInMelee)) return; + oTarget = ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee); + if (oTarget != OBJECT_INVALID) + { + if (ai_TryMeleeTalents (oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_shadow.nss b/_module/nss/ai_shadow.nss new file mode 100644 index 00000000..3d6419fa --- /dev/null +++ b/_module/nss/ai_shadow.nss @@ -0,0 +1,77 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_shadow +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures that are incorporeal. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + // Shadows do not flee! if(nInMelee && ai_MoralCheck()) 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 *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************ RANGED ATTACKS ******************************* + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + string sIndex; + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(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 ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee); + 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_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); + } + } + else ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_sorcerer.nss b/_module/nss/ai_sorcerer.nss new file mode 100644 index 00000000..f1fdcc3f --- /dev/null +++ b/_module/nss/ai_sorcerer.nss @@ -0,0 +1,61 @@ +/*//////////////////////////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_sorcerer +////////////////////////////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Sorcerer. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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 *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************** RANGED ATTACKS ***************************** + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE, OBJECT_INVALID, AI_RANGE_CLOSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************* MELEE ATTACKS ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee, FALSE); + // I have a target now lets see if we want to move in! + if(oTarget != OBJECT_INVALID) ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + else ai_SearchForHiddenCreature(oCreature, TRUE, OBJECT_INVALID, AI_RANGE_CLOSE); +} diff --git a/_module/nss/ai_taunter.nss b/_module/nss/ai_taunter.nss new file mode 100644 index 00000000..825d0259 --- /dev/null +++ b/_module/nss/ai_taunter.nss @@ -0,0 +1,78 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: ai_taunter +//////////////////////////////////////////////////////////////////////////////// + ai script for creatures using defined to use the taunt skill. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck (oCreature)) 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; + //**************************** 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; + } + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // *************************** RANGED ATTACKS ***************************** + // We use a bow when we are not in melee, or only 1 enemy with PBS. + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if (!nInMelee) oTarget = ai_GetNearestTarget (oCreature); + else oTarget = ai_GetNearestTarget (oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + if(ai_TryRapidShotFeat(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 *************************** + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat (oCreature, nInMelee); + if (oTarget != OBJECT_INVALID) + { + if (ai_TryTaunt (oCreature, oTarget)) return; + if (ai_TryMeleeTalents (oCreature, oTarget)) return; + ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + } + else ai_SearchForHiddenCreature(oCreature, TRUE); +} diff --git a/_module/nss/ai_wizard.nss b/_module/nss/ai_wizard.nss new file mode 100644 index 00000000..6baa4c8c --- /dev/null +++ b/_module/nss/ai_wizard.nss @@ -0,0 +1,63 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: ai_wizard +//////////////////////////////////////////////////////////////////////////////// + ai script for creatures using the class Wizard. + OBJECT_SELF is the creature 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); + //*************************** HEALING & CURES **************************** + if(ai_TryHealingTalent(oCreature, nInMelee)) return; + if(ai_TryCureConditionTalent(oCreature, nInMelee)) return; + if(nInMelee && ai_MoralCheck(oCreature)) 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; + // ************************** CLASS FEATURES **************************** + if(GetLocalInt(GetModule(), AI_RULE_SUMMON_COMPANIONS) && ai_TrySummonFamiliarTalent(oCreature)) return; + //************************** DEFENSIVE TALENTS *************************** + int nRound = ai_GetCurrentRound(oCreature); + 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; + // PHYSICAL ATTACKS - Either we don't have talents or we are saving them. + // ************************** RANGED ATTACKS ***************************** + object oTarget; + if(ai_CanIUseRangedWeapon(oCreature, nInMelee)) + { + if(ai_HasRangedWeaponWithAmmo(oCreature)) + { + if(!nInMelee) oTarget = ai_GetNearestTarget(oCreature); + else oTarget = ai_GetNearestTarget(oCreature, AI_RANGE_MELEE); + if(oTarget != OBJECT_INVALID) + { + ai_ActionAttack(oCreature, AI_LAST_ACTION_RANGED_ATK, oTarget, nInMelee, TRUE); + return; + } + else + { + ai_SearchForHiddenCreature(oCreature, TRUE, OBJECT_INVALID, AI_RANGE_CLOSE); + return; + } + } + else if(ai_InCombatEquipBestRangedWeapon(oCreature)) return; + } + // ************************* MELEE ATTACKS ******************************* + if(ai_InCombatEquipBestMeleeWeapon(oCreature)) return; + oTarget = ai_GetNearestTargetForMeleeCombat(oCreature, nInMelee, TRUE); + // I have a target now lets see if we want to move in! + if(oTarget != OBJECT_INVALID) ai_ActionAttack(oCreature, AI_LAST_ACTION_MELEE_ATK, oTarget); + else ai_SearchForHiddenCreature(oCreature, TRUE, OBJECT_INVALID, AI_RANGE_CLOSE); +} diff --git a/_module/nss/dk_handler.nss b/_module/nss/dk_handler.nss index 5592e671..d60aaf53 100644 --- a/_module/nss/dk_handler.nss +++ b/_module/nss/dk_handler.nss @@ -186,7 +186,7 @@ object oItem = GetFirstItemInInventory(oObject); } return TRUE; } -int GetIsDestroyable(object oObject) +int mod_GetIsDestroyable(object oObject) { if(GetIsImportant(oObject)) return FALSE; diff --git a/_module/nss/hif_onclientente.nss b/_module/nss/hif_onclientente.nss index 871bf30a..dc9d9141 100644 --- a/_module/nss/hif_onclientente.nss +++ b/_module/nss/hif_onclientente.nss @@ -14,6 +14,9 @@ void main() //: Add system journal entries AddJournalQuestEntry("xprules", 1, oPC, FALSE, FALSE, FALSE); AddJournalQuestEntry("lvl_adj", 1, oPC, FALSE, FALSE, FALSE); + AddJournalQuestEntry("JRNL_PRC8", 1, oPC, FALSE, FALSE, FALSE); + + ExecuteScript("0e_onclientload", oPC); //:: Send Under Construction message SendMessageToPC(oPC, "This server is under active development and could go down arbitrarily. Admin will try to give notice beforehand."); diff --git a/_module/nss/mm_prc_spells.nss b/_module/nss/mm_prc_spells.nss new file mode 100644 index 00000000..4aeb8e03 --- /dev/null +++ b/_module/nss/mm_prc_spells.nss @@ -0,0 +1,161 @@ +#include "0i_menus" +// Does startup check if the game has just been loaded. +int StartingUp(object oPC); +json ai_CheckToReplaceSpell(json jSpellList, int nClass, int nLevel, int nSlot) +{ + //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)); + if(nSpell > 0) + { + //WriteTimestampedLogEntry("mm_prc_spells, 13 nSpell: " + IntToString(nSpell) + + // " nLevel: " + IntToString(nLevel) + " nSlot: " + IntToString(nSlot)); + json jSpellArray = JsonArrayGet(jSpellList, nSlot); + json jSpell = JsonObjectGet(jSpellArray, "Spell"); + jSpell = JsonObjectSet(jSpell, "value", JsonInt(nSpell)); + jSpellArray = JsonObjectSet(jSpellArray, "Spell", jSpell); + return JsonArraySet(jSpellList, nSlot, jSpellArray); + } + return jSpellList; +} +void main() +{ + object oPC = OBJECT_SELF; + if(StartingUp(oPC)) return; + int bChanged, bCreatureChanged, nPosition, nClass, nLevel, nSlot, nMaxSlots; + json jClass, jMemorizedList, jKnownList; + object oModule = GetModule(); + json jCreature = GetLocalJson(oModule, AI_MONSTER_JSON); + object oCreature = GetLocalObject(oModule, AI_MONSTER_OBJECT); + json jClassList = GffGetList(jCreature, "ClassList"); + while(nPosition <= AI_MAX_CLASSES_PER_CHARACTER) + { + nClass = GetClassByPosition(nPosition, oCreature); + if(Get2DAString("classes", "SpellCaster", nClass) == "1") + { + //WriteTimestampedLogEntry("mm_prc_spells, 39 " + GetName(oCreature) + JsonDump(jClassList, 4)); + jClass = JsonArrayGet(jClassList, nPosition - 1); + if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") + { + nLevel = 1; + while(nLevel < 9) + { + jMemorizedList = GffGetList(jClass, "MemorizedList" + IntToString(nLevel)); + if(JsonGetType(jMemorizedList) != JSON_TYPE_NULL) + { + nSlot = 0; + nMaxSlots = GetMemorizedSpellCountByLevel(oCreature, nClass, nLevel); + while(nSlot < nMaxSlots) + { + jMemorizedList = ai_CheckToReplaceSpell(jMemorizedList, nClass, nLevel, nSlot); + nSlot++; + } + //WriteTimestampedLogEntry("nClass: " + IntToString(nClass) + " nLevel: " + IntToString(nLevel) + + // " nSlot: " + IntToString(nSlot) + " jMemorizedList " + JsonDump(jMemorizedList, 4)); + jClass = GffReplaceList(jClass, "MemorizedList" + IntToString(nLevel), jMemorizedList); + bChanged = TRUE; + } + nLevel++; + } + } + else + { + nLevel = 1; + while(nLevel < 9) + { + jKnownList = GffGetList(jClass, "KnownList" + IntToString(nLevel)); + if(JsonGetType(jMemorizedList) != JSON_TYPE_NULL) + { + nSlot = 0; + nMaxSlots = GetKnownSpellCount(oCreature, nClass, nLevel); + while(nSlot < nMaxSlots) + { + jKnownList = ai_CheckToReplaceSpell(jKnownList, nClass, nLevel, nSlot); + nSlot++; + } + jClass = GffReplaceList(jClass, "KnownList" + IntToString(nLevel), jKnownList); + bChanged = TRUE; + } + nLevel++; + } + } + if(bChanged) + { + //WriteTimestampedLogEntry("0i_module, 87 " + GetName(oCreature) + " jClass: " + JsonDump(jClass, 4)); + jClassList = JsonArraySet(jClassList, nPosition - 1, jClass); + //if(AI_DEBUG) ai_Debug("0i_module, 89 " + GetName(oCreature) + " jClassList: " + JsonDump(jClassList, 4)); + jCreature = GffReplaceList(jCreature, "ClassList", jClassList); + bCreatureChanged = TRUE; + bChanged = FALSE; + } + } + nPosition++; + } + if(bCreatureChanged) + { + //WriteTimestampedLogEntry("mm_prc_spells, 99 " + GetName(oCreature) + " jClassList: " + JsonDump(jClassList, 4)); + SetLocalJson(oModule, AI_MONSTER_JSON, jCreature); + SetLocalInt(oModule, AI_MONSTER_CHANGED, TRUE); + } +} +int PRCSpellsSetup(object oPC) +{ + // Check to make sure prc_add_spells.2da is loaded. + if(ResManGetAliasFor("prc_add_spells", RESTYPE_2DA) == "") + { + SendMessageToPC(oPC, "prc_add_spells.2da is not loaded! Make sure it is in the override or development folder."); + return FALSE; + } + // Check to make sure PRC is loaded. + if(!GetLocalInt(GetModule(), AI_USING_PRC)) + { + SendMessageToPC(oPC, "PRC is not being used. PRC must be active for this mod to work."); + return FALSE; + } + return TRUE; +} +void SetMonsterModJson(object oPC) +{ + object oModule = GetModule(); + json jMonsterMods = GetLocalJson(oModule, AI_MONSTER_MOD_JSON); + if(JsonGetType(jMonsterMods) == JSON_TYPE_NULL) jMonsterMods = JsonArray(); + int nIndex; + string sMonsterMod = JsonGetString(JsonArrayGet(jMonsterMods, nIndex)); + while(sMonsterMod != "") + { + if(sMonsterMod == "mm_prc_spells") return; + sMonsterMod = JsonGetString(JsonArrayGet(jMonsterMods, ++nIndex)); + } + jMonsterMods = JsonArrayInsert(jMonsterMods, JsonString("mm_prc_spells")); + SetLocalJson(oModule, AI_MONSTER_MOD_JSON, jMonsterMods); + ai_SendMessages("mm_prc_spells loaded! Monsters will be using PRC spells.", AI_COLOR_YELLOW, oPC); +} +int StartingUp(object oPC) +{ + if(!PRCSpellsSetup(oPC)) + { + SendMessageToPC(oPC, "mm_prc_spells monster mod has failed to load due to an error."); + // Return -1 in AI_PLUGIN_SET to tell PEPS that we failed to load. + SetLocalInt(oPC, AI_PLUGIN_SET, -1); + return TRUE; + } + if(GetLocalInt(oPC, AI_ADD_PLUGIN)) + { + json jPlugin = JsonArray(); + jPlugin = JsonArrayInsert(jPlugin, JsonString("mm_prc_spells")); + jPlugin = JsonArrayInsert(jPlugin, JsonInt(3)); + jPlugin = JsonArrayInsert(jPlugin, JsonString("Monsters will use PRC spells!")); + jPlugin = JsonArrayInsert(jPlugin, JsonString("")); + json jPlugins = GetLocalJson(oPC, AI_JSON_PLUGINS); + jPlugins = JsonArrayInsert(jPlugins, jPlugin); + SetLocalJson(oPC, AI_JSON_PLUGINS, jPlugin); + SetLocalInt(oPC, AI_PLUGIN_SET, TRUE); + SetMonsterModJson(oPC); + return TRUE; + } + if(!GetLocalInt(oPC, AI_STARTING_UP)) return FALSE; + SetMonsterModJson(oPC); + return TRUE; +} + diff --git a/_module/nss/nw_c2_default1.nss b/_module/nss/nw_c2_default1.nss index fe392859..6d717da3 100644 --- a/_module/nss/nw_c2_default1.nss +++ b/_module/nss/nw_c2_default1.nss @@ -1,107 +1,92 @@ -//::////////////////////////////////////////////////// -//:: NW_C2_DEFAULT1 -/* - Default OnHeartbeat script for NPCs. - - This script causes NPCs to perform default animations - while not otherwise engaged. - - This script duplicates the behavior of the default - script and just cleans up the code and removes - redundant conditional checks. - - */ -//::////////////////////////////////////////////////// -//:: Copyright (c) 2002 Floodgate Entertainment -//:: Created By: Naomi Novik -//:: Created On: 12/22/2002 -//::////////////////////////////////////////////////// - -#include "nw_i0_generic" - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_default1 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnHeartbeat script; + This will usually fire every 6 seconds (1 game round). +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_module" void main() { - - // * if not runnning normal or better Ai then exit for performance reasons - if (GetAILevel() == AI_LEVEL_VERY_LOW) return; - - ExecuteScript("prc_npc_hb", OBJECT_SELF); - - // Buff ourselves up right away if we should - if(GetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY)) - { - // This will return TRUE if an enemy was within 40.0 m - // and we buffed ourselves up instantly to respond -- - // simulates a spellcaster with protections enabled - // already. - if(TalentAdvancedBuff(40.0)) - { - // This is a one-shot deal - SetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY, FALSE); - - // This return means we skip sending the user-defined - // heartbeat signal in this one case. - return; - } - } - - - if(GetHasEffect(EFFECT_TYPE_SLEEP)) + // If not runnning normal or better AI then exit for performance reasons + if (GetAILevel(OBJECT_SELF) == AI_LEVEL_VERY_LOW) return; + 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 + // OnSpawn for their own use. If we have to recreate the creature then we + // skip the rest of the heartbeat since this version is being destroyed! + if(ai_OnMonsterSpawn(oCreature)) return; + if(AI_DEBUG) ai_Debug("nw_c2_default1", "16", GetName(oCreature) + " Heartbeat." + + " Searching: " + IntToString(GetLocalInt(oCreature, AI_AM_I_SEARCHING))); + if(ai_GetHasEffectType(oCreature, EFFECT_TYPE_SLEEP)) { // If we're asleep and this is the result of sleeping // at night, apply the floating 'z's visual effect // every so often - if(GetSpawnInCondition(NW_FLAG_SLEEPING_AT_NIGHT)) { effect eVis = EffectVisualEffect(VFX_IMP_SLEEP); if(d10() > 6) { - ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF); + ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oCreature); } } } - - // If we have the 'constant' waypoints flag set, walk to the next - // waypoint. - else if ( GetWalkCondition(NW_WALK_FLAG_CONSTANT) ) - { - WalkWayPoints(); - } - - // Check to see if we should be playing default animations - // - make sure we don't have any current targets - else if ( !GetIsObjectValid(GetAttemptedAttackTarget()) - && !GetIsObjectValid(GetAttemptedSpellTarget()) - // && !GetIsPostOrWalking()) - && !GetIsObjectValid(GetNearestSeenEnemy())) - { - if (GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL) || GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE) || - GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE)) - { - // This handles special attacking/fleeing behavior - // for omnivores & herbivores. - DetermineSpecialBehavior(); - } - else if (!IsInConversation(OBJECT_SELF)) - { - if (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS) - || GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN) - || GetIsEncounterCreature()) - { - PlayMobileAmbientAnimations(); - } - else if (GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS)) - { - PlayImmobileAmbientAnimations(); - } - } - } - - // Send the user-defined event signal if specified + // Send the user-defined event signal if specified here so it doesn't get skipped. if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT)) { - SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_HEARTBEAT)); + SignalEvent(oCreature, EventUserDefined(EVENT_HEARTBEAT)); } + if(ai_GetIsBusy(oCreature) || ai_Disabled(oCreature) || + GetLocalInt(oCreature, AI_AM_I_SEARCHING)) return; + if(ai_GetIsInCombat(oCreature)) + { + if(ai_GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE)) + { + object oTarget = ai_GetNearestEnemy(oCreature, 1, 7, 7, -1, -1, TRUE); + if(GetDistanceBetween(oCreature, oTarget) <= 6.0) + { + if(GetLevelByClass(CLASS_TYPE_DRUID, oTarget) == 0 && GetLevelByClass(CLASS_TYPE_RANGER, oTarget) == 0) + { + SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_coward"); + ActionMoveAwayFromObject(oTarget, TRUE, AI_RANGE_LONG); + return; + } + } + } + ai_DoMonsterCombatRound(oCreature); + return; + } + if(ai_CheckForCombat(oCreature, TRUE)) return; + // If we have not set up our talents then we need to check to see if we should. + if(!GetLocalInt(oCreature, AI_TALENTS_SET)) + { + // We setup our talents when a PC gets withing Battlefield range 40.0 meters. + object oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oCreature, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); + if(oPC != OBJECT_INVALID && GetDistanceBetween(oCreature, oPC) <= AI_RANGE_BATTLEFIELD) + { + if(AI_DEBUG) ai_Debug("nw_c2_default1", "72", GetName(oCreature) + " is " + + FloatToString(GetDistanceBetween(oCreature, oPC), 0, 2) + " from " + GetName(oPC)); + if(AI_DEBUG) ai_Debug("nw_c2_default1", "74", GetName(oCreature) + " is Setting Creature Talents and buffing!"); + ai_SetupMonsterBuffTargets(oCreature); + // To save steps and time we set the talents while we buff! + ai_SetCreatureTalents(oCreature, TRUE); + 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; } diff --git a/_module/nss/nw_c2_default2.nss b/_module/nss/nw_c2_default2.nss index f9e873ce..fa6f5f82 100644 --- a/_module/nss/nw_c2_default2.nss +++ b/_module/nss/nw_c2_default2.nss @@ -1,165 +1,138 @@ -//::////////////////////////////////////////////////// -//:: NW_C2_DEFAULT2 -/* - Default OnPerception event handler for NPCs. - - Handles behavior when perceiving a creature for the - first time. - */ -//::////////////////////////////////////////////////// - -#include "nw_i0_generic" - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_default2 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnPerception script when not in combat; + There are 4 types of perception - Heard, Inaudible, Seen, Vanished. + Only one type will ever be true in an event trigger. + The order of trigger is Heard/Seen and Inaudible/Vanished. + There are two states of percepion Heard and Seen. + These states can be set at the same time thus a heard event can see the creature. + Fires when ever one of these states changes from TRUE to FALSE or FALSE to TRUE. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" void main() { - // * if not runnning normal or better Ai then exit for performance reasons - if (GetAILevel() == AI_LEVEL_VERY_LOW) return; - - ExecuteScript("prc_npc_percep", OBJECT_SELF); - - object oPercep = GetLastPerceived(); + // * 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", 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()) + { + if(AI_DEBUG) ai_Debug("nw_c2_default2", "22", GetName(oCreature) + " sees " + + GetName(GetLastPerceived()) + " Distance: " + + FloatToString(GetDistanceBetween(GetLastPerceived(), oCreature), 0, 2) + "."); + } + if(GetLastPerceptionHeard()) + { + if(AI_DEBUG) ai_Debug("nw_c2_default2", "28", GetName(oCreature) + " heard " + + GetName(GetLastPerceived()) + " Distance: " + + FloatToString(GetDistanceBetween(GetLastPerceived(), oCreature), 0, 2) + "."); + } + if(GetLastPerceptionVanished ()) + { + if(AI_DEBUG) ai_Debug("nw_c2_default2", "34", GetName(oCreature) + " lost sight of " + + GetName(GetLastPerceived ()) + "."); + } + // We do nothing on Inaudibles so drop out early! + if(GetLastPerceptionInaudible()) + { + if(AI_DEBUG) ai_Debug("nw_c2_default2", "41", GetName(oCreature) + " lost sound of " + + GetName(GetLastPerceived()) + "."); + return; + } + object oLastPerceived = GetLastPerceived(); + if(AI_DEBUG) ai_Debug("nw_c2_default2", "45", "Dead? " + IntToString(GetIsDead(oLastPerceived)) + + " Enemy? " + IntToString(GetIsEnemy(oLastPerceived, oCreature))); + if(ai_Disabled(oCreature)) return; + if(GetIsDead(oLastPerceived)) return; int bSeen = GetLastPerceptionSeen(); - int bHeard = GetLastPerceptionHeard(); - if (bHeard == FALSE) + // This will cause all NPC's to speak their one-liner conversation + // on perception even if they are already in combat. + if(GetIsPC(oLastPerceived) && bSeen) { - // Has someone vanished in front of me? - bHeard = GetLastPerceptionVanished(); - } - - // This will cause the NPC to speak their one-liner - // conversation on perception even if they are already - // in combat. - if(GetSpawnInCondition(NW_FLAG_SPECIAL_COMBAT_CONVERSATION) - && GetIsPC(oPercep) - && bSeen) - { - SpeakOneLinerConversation(); - } - - // March 5 2003 Brent - // Had to add this section back in, since modifications were not taking this specific - // example into account -- it made invisibility basically useless. - //If the last perception event was hearing based or if someone vanished then go to search mode - if ((GetLastPerceptionVanished()) && GetIsEnemy(GetLastPerceived())) - { - object oGone = GetLastPerceived(); - if((GetAttemptedAttackTarget() == GetLastPerceived() || - GetAttemptedSpellTarget() == GetLastPerceived() || - GetAttackTarget() == GetLastPerceived()) && GetArea(GetLastPerceived()) != GetArea(OBJECT_SELF)) + if(GetSpawnInCondition(NW_FLAG_SPECIAL_COMBAT_CONVERSATION)) { - ClearAllActions(); - DetermineCombatRound(); + SpeakOneLinerConversation(); } } - - // This section has been heavily revised while keeping the - // pre-existing behavior: - // - If we're in combat, keep fighting. - // - If not and we've perceived an enemy, start to fight. - // Even if the perception event was a 'vanish', that's - // still what we do anyway, since that will keep us - // fighting any visible targets. - // - If we're not in combat and haven't perceived an enemy, - // see if the perception target is a PC and if we should - // speak our attention-getting one-liner. - if (GetIsInCombat(OBJECT_SELF)) + if(GetIsEnemy(oLastPerceived, oCreature)) { - // don't do anything else, we're busy - //MyPrintString("GetIsFighting: TRUE"); - - } - // * BK FEB 2003 Only fight if you can see them. DO NOT RELY ON HEARING FOR ENEMY DETECTION - else if (GetIsEnemy(oPercep) && bSeen) - { // SpawnScriptDebugger(); - //MyPrintString("GetIsEnemy: TRUE"); - // We spotted an enemy and we're not already fighting - if(!GetHasEffect(EFFECT_TYPE_SLEEP)) { - if(GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) + // ************************** ENEMY SEEN ******************************* + if(bSeen) + { + // If the creature we are perceiving was our invisible creature then + // remove that they are invisible. + if(oLastPerceived == GetLocalObject(oCreature, AI_IS_INVISIBLE)) { - //MyPrintString("DetermineSpecialBehavior"); - DetermineSpecialBehavior(); - } else - { - //MyPrintString("DetermineCombatRound"); - SetFacingPoint(GetPosition(oPercep)); - SpeakString("NW_I_WAS_ATTACKED", TALKVOLUME_SILENT_TALK); - DetermineCombatRound(); + DeleteLocalObject(oCreature, AI_IS_INVISIBLE); } + ai_MonsterEvaluateNewThreat(oCreature, oLastPerceived, AI_I_SEE_AN_ENEMY); } + // ************************** ENEMY HEARD ****************************** + else if(GetLastPerceptionHeard()) + { + ai_MonsterEvaluateNewThreat(oCreature, oLastPerceived, AI_I_HEARD_AN_ENEMY); + } + // ************************** ENEMY VANISHED *************************** + else if(GetLastPerceptionVanished()) + { + // Lets keep a mental note of the invisible creature. + SetLocalObject(oCreature, AI_IS_INVISIBLE, oLastPerceived); + if(AI_DEBUG) ai_Debug("0e_c2_2_percept", "82", " We saw " + GetName(oLastPerceived) + " disappear!"); + if(ai_GetIsBusy(oCreature)) return; + // If in combat check to see if our target disappeared. + // If they have and we are not in melee with them then reevaluate combat + // since we lost our target. + if(ai_GetIsInCombat(oCreature)) + { + if(AI_DEBUG) ai_Debug("nw_c2_default2", "89", "Is this our target? " + + IntToString(ai_GetAttackedTarget(oCreature, TRUE, TRUE) == oLastPerceived)); + if(ai_GetAttackedTarget(oCreature, TRUE, TRUE) == oLastPerceived) + { + ai_DoMonsterCombatRound(oCreature); + } + } + // We are not in combat so lets move to that location and check it out. + else ActionMoveToLocation(GetLocation(oLastPerceived), TRUE); + // we use to move to the object but thats a bit creepy! + //else ActionMoveToObject(oLastPerceived, TRUE, AI_RANGE_CLOSE); + } + // ************************ ENEMY INAUDIBLE***************************** + // Not used. } else { - if (bSeen) + // ************************ NON_ENEMY SEEN ***************************** + if(bSeen) { - //MyPrintString("GetLastPerceptionSeen: TRUE"); - if(GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) { - DetermineSpecialBehavior(); - } else if (GetSpawnInCondition(NW_FLAG_SPECIAL_CONVERSATION) - && GetIsPC(oPercep)) + if(ai_GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) ai_DetermineSpecialBehavior(oCreature); + else if(GetSpawnInCondition(NW_FLAG_SPECIAL_CONVERSATION) && GetIsPC(oLastPerceived)) { - // The NPC will speak their one-liner conversation - // This should probably be: - // SpeakOneLinerConversation(oPercep); - // instead, but leaving it as is for now. - ActionStartConversation(OBJECT_SELF); + ActionStartConversation(oCreature); } } - else - // * July 14 2003: Some minor reactions based on invisible creatures being nearby - if (bHeard && GetIsEnemy(oPercep)) - { - // SpeakString("vanished"); - // * don't want creatures wandering too far after noises - if (GetDistanceToObject(oPercep) <= 7.0) - { -// if (GetHasSpell(SPELL_TRUE_SEEING) == TRUE) - if (GetHasSpell(SPELL_TRUE_SEEING)) - { - ActionCastSpellAtObject(SPELL_TRUE_SEEING, OBJECT_SELF); - } - else -// if (GetHasSpell(SPELL_SEE_INVISIBILITY) == TRUE) - if (GetHasSpell(SPELL_SEE_INVISIBILITY)) - { - ActionCastSpellAtObject(SPELL_SEE_INVISIBILITY, OBJECT_SELF); - } - else -// if (GetHasSpell(SPELL_INVISIBILITY_PURGE) == TRUE) - if (GetHasSpell(SPELL_INVISIBILITY_PURGE)) - { - ActionCastSpellAtObject(SPELL_INVISIBILITY_PURGE, OBJECT_SELF); - } - else - { - ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_LEFT, 0.5); - ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_RIGHT, 0.5); - ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD, 0.5); - } - } - } - - // activate ambient animations or walk waypoints if appropriate - if (!IsInConversation(OBJECT_SELF)) { - if (GetIsPostOrWalking()) { - WalkWayPoints(); - } else if (GetIsPC(oPercep) && - (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS) - || GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN) - || GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS) - || GetIsEncounterCreature())) - { - SetAnimationCondition(NW_ANIM_FLAG_IS_ACTIVE); - } - } } - + if(!IsInConversation(oCreature)) + { + if(GetIsPostOrWalking()) + { + WalkWayPoints(); + } + else if(GetIsPC(oLastPerceived) && + (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS) || + GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN) || + GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS) || + GetIsEncounterCreature())) + { + SetAnimationCondition(NW_ANIM_FLAG_IS_ACTIVE); + } + } // Send the user-defined event if appropriate - if(GetSpawnInCondition(NW_FLAG_PERCIEVE_EVENT) && GetLastPerceptionSeen()) + if(GetSpawnInCondition(NW_FLAG_PERCIEVE_EVENT) && bSeen) { SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_PERCEIVE)); } } - - - - diff --git a/_module/nss/nw_c2_default3.nss b/_module/nss/nw_c2_default3.nss index 572e1239..6e364125 100644 --- a/_module/nss/nw_c2_default3.nss +++ b/_module/nss/nw_c2_default3.nss @@ -1,56 +1,68 @@ -//:://///////////////////////////////////////////// -//:: Default: End of Combat Round -//:: NW_C2_DEFAULT3 -//:: Copyright (c) 2008 Bioware Corp. -//::////////////////////////////////////////////// -/* - Calls the end of combat script every round -*/ -//::////////////////////////////////////////////// -//:: Created By: Preston Watamaniuk -//:: Created On: Oct 16, 2001 -//::////////////////////////////////////////////// -//::////////////////////////////////////////////// -//:: Modified By: Deva Winblood -//:: Modified On: Feb 16th, 2008 -//:: Added Support for Mounted Combat Feat Support -//::////////////////////////////////////////////// - -#include "NW_I0_GENERIC" - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_default3 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnCombatRoundEnd event script; + Fires at the end of each combat round (6 seconds). + This will fire as long as oCreature is in combat (GetIsInCombat()). + This event starts counting once a combat action is started. + Every time a spell is cast it will queue another end combat round so haste with + two spells cast will fire this twice in one round. + It will also fire at the end of a hostile effect that stops actions i.e Stunned, Knockdown etc. + Action modes are also cleared prior to this event executing! + GetAttemptedAttackTarget() & GetAttemptedSpellTarget() also get cleared prior to this event. + This event can be canceled with ClearAllActions(TRUE) and SurrenderToEnemies. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" void main() { - ExecuteScript("prc_npc_combat", OBJECT_SELF); - - if (!GetLocalInt(GetModule(),"X3_NO_MOUNTED_COMBAT_FEAT")) - { // set variables on target for mounted combat - DeleteLocalInt(OBJECT_SELF,"bX3_LAST_ATTACK_PHYSICAL"); - DeleteLocalInt(OBJECT_SELF,"nX3_HP_BEFORE"); - DeleteLocalInt(OBJECT_SELF,"bX3_ALREADY_MOUNTED_COMBAT"); - if (GetHasFeat(FEAT_MOUNTED_COMBAT,OBJECT_SELF)) - { // check for AC increase - int nRoll=d20()+GetSkillRank(SKILL_RIDE); - nRoll=nRoll-10; - if (nRoll>4) - { // ac increase - nRoll=nRoll/5; - ApplyEffectToObject(DURATION_TYPE_TEMPORARY,EffectACIncrease(nRoll),OBJECT_SELF,8.5); - } // ac increase - } // check for AC increase - } // set variables on target for mounted combat - - if(GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) - { - DetermineSpecialBehavior(); - } - else if(!GetSpawnInCondition(NW_FLAG_SET_WARNINGS)) - { - DetermineCombatRound(); - } + object oCreature = 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)); } + if(ai_Disabled(oCreature)) return; + // Action modes get cleared prior to each OnCombatRoundEnd! + // We do this to keep the action mode going. + int nActionMode = GetLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + if(nActionMode > 0) + { + SetActionMode(oCreature, nActionMode, TRUE); + // We don't want to use up all of the Dwarven Defenders uses! + if(nActionMode == 12) IncrementRemainingFeatUses(oCreature, FEAT_DWARVEN_DEFENDER_DEFENSIVE_STANCE); + } + int nAction = GetCurrentAction(oCreature); + if(AI_DEBUG) ai_Debug("nw_c2_default3", "37", "nAction: " + IntToString(nAction)); + switch(nAction) + { + // These actions are uninteruptable. + case ACTION_MOVETOPOINT : + case ACTION_CASTSPELL : + case ACTION_ITEMCASTSPELL : + case ACTION_COUNTERSPELL : return; + // Might be doing a special action that is not a defined action. + case ACTION_INVALID : + { + int nCombatWait = GetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + if(AI_DEBUG) ai_Debug("nw_c2_default3", "49", "nCombatWait: " + IntToString(nCombatWait)); + if(nCombatWait) + { + if(ai_IsInCombatRound(oCreature, nCombatWait)) return; + DeleteLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + } + } + // We always want to interupt an attack action at the end of a round. + //case ACTION_ATTACKOBJECT : + } + if(ai_GetIsInCombat(oCreature)) + { + ai_DoAssociateCombatRound (oCreature); + return; + } + if(ai_GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) ai_DetermineSpecialBehavior(oCreature); } diff --git a/_module/nss/nw_c2_default4.nss b/_module/nss/nw_c2_default4.nss index 256e0bb2..38603f10 100644 --- a/_module/nss/nw_c2_default4.nss +++ b/_module/nss/nw_c2_default4.nss @@ -1,92 +1,70 @@ -//::////////////////////////////////////////////////// -//:: NW_C2_DEFAULT4 -/* - Default OnConversation event handler for NPCs. - - */ -//::////////////////////////////////////////////////// -//:: Copyright (c) 2002 Floodgate Entertainment -//:: Created By: Naomi Novik -//:: Created On: 12/22/2002 -//::////////////////////////////////////////////////// - -#include "nw_i0_generic" - +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_c2_4_convers + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnConversation; + Fires when oCreature has been clicked on for conversation. + Fires when oCreature hears a shout from another creature. + If SetListening is FALSE then oCreature will not "hear" anything. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void ai_MonsterCommands(object oCreature, object oSpeaker, int nMatch); void main() { - ExecuteScript("prc_npc_conv", OBJECT_SELF); - - // * if petrified, jump out - if (GetHasEffect(EFFECT_TYPE_PETRIFY, OBJECT_SELF) == TRUE) + object oCreature = OBJECT_SELF; + 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))); + if(ai_GetIsBusy(oCreature) || ai_Disabled(oCreature) || GetLocalInt(oCreature, AI_AM_I_SEARCHING)) return; + if(ai_GetIsInCombat(oCreature)) { + ai_DoMonsterCombatRound(oCreature); return; } - - // * If dead, exit directly. - if (GetIsDead(OBJECT_SELF) == TRUE) - { - return; - } - - // See if what we just 'heard' matches any of our - // predefined patterns + object oLastSpeaker = GetLastSpeaker(); int nMatch = GetListenPatternNumber(); - object oShouter = GetLastSpeaker(); - - if (nMatch == -1) + if(nMatch != -1) { - // Not a match -- start an ordinary conversation - if (GetCommandable(OBJECT_SELF)) - { - ClearActions(CLEAR_NW_C2_DEFAULT4_29); - BeginConversation(); - } - else - // * July 31 2004 - // * If only charmed then allow conversation - // * so you can have a better chance of convincing - // * people of lowering prices - if (GetHasEffect(EFFECT_TYPE_CHARMED) == TRUE) - { - ClearActions(CLEAR_NW_C2_DEFAULT4_29); - BeginConversation(); - } + if(GetFactionEqual(oLastSpeaker, oCreature)) ai_MonsterCommands(oCreature, oLastSpeaker, nMatch); } - // Respond to shouts from friendly non-PCs only - else if (GetIsObjectValid(oShouter) - && !GetIsPC(oShouter) - && GetIsFriend(oShouter)) + else { - object oIntruder = OBJECT_INVALID; - // Determine the intruder if any - if(nMatch == 4) - { - oIntruder = GetLocalObject(oShouter, "NW_BLOCKER_INTRUDER"); - } - else if (nMatch == 5) - { - oIntruder = GetLastHostileActor(oShouter); - if(!GetIsObjectValid(oIntruder)) - { - oIntruder = GetAttemptedAttackTarget(); - if(!GetIsObjectValid(oIntruder)) - { - oIntruder = GetAttemptedSpellTarget(); - if(!GetIsObjectValid(oIntruder)) - { - oIntruder = OBJECT_INVALID; - } - } - } - } - - // Actually respond to the shout - RespondToShout(oShouter, nMatch, oIntruder); + ai_ClearCreatureActions(); + BeginConversation(); } - // Send the user-defined event if appropriate if(GetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT)) { SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_DIALOGUE)); } } +void ai_MonsterCommands(object oCreature, object oSpeaker, int nMatch) +{ + object oTarget = GetLocalObject(oSpeaker, AI_MY_TARGET); + if(nMatch == AI_ALLY_SEES_AN_ENEMY || nMatch == AI_ALLY_HEARD_AN_ENEMY) + { + if(AI_DEBUG) ai_Debug("nw_c2_default4", "46", GetName(oCreature) + " heard " + + GetName(oSpeaker) + " has seen an enemy!"); + if(ai_CanIAttack(oCreature)) ai_FindTheEnemy(oCreature, oSpeaker, oTarget, TRUE); + } + else if(nMatch == AI_ALLY_ATKED_BY_WEAPON || + nMatch == AI_ALLY_ATKED_BY_SPELL) + { + if(AI_DEBUG) ai_Debug("nw_c2_default4", "53", GetName(oCreature) + " heard " + + GetName(oSpeaker) + " has been attacked by " + + GetName(GetLocalObject(oSpeaker, AI_MY_TARGET)) + "!"); + if(ai_CanIAttack(oCreature)) ai_FindTheEnemy(oCreature, oSpeaker, oTarget, TRUE); + } + else if(nMatch == AI_ALLY_IS_WOUNDED) + { + if(AI_DEBUG) ai_Debug("nw_c2_default4", "60", GetName(oCreature) + " heard " + + GetName(oSpeaker) + " is wounded!"); + if(ai_GetIsInCombat(oCreature)) ai_TryHealingTalent(oCreature, ai_GetNumOfEnemiesInRange(oCreature), oSpeaker); + else ai_TryHealing(oCreature, oSpeaker); + } + /*else if(nMatch == AI_ALLY_IS_DEAD) + { + } */ +} + diff --git a/_module/nss/nw_c2_default5.nss b/_module/nss/nw_c2_default5.nss index d4857fbd..c2e663dd 100644 --- a/_module/nss/nw_c2_default5.nss +++ b/_module/nss/nw_c2_default5.nss @@ -1,71 +1,37 @@ -//:://///////////////////////////////////////////// -//:: Default On Attacked -//:: NW_C2_DEFAULT5 -//:: Copyright (c) 2001 Bioware Corp. -//::////////////////////////////////////////////// -/* - If already fighting then ignore, else determine - combat round -*/ -//::////////////////////////////////////////////// -//:: Created By: Preston Watamaniuk -//:: Created On: Oct 16, 2001 -//::////////////////////////////////////////////// -//::////////////////////////////////////////////// -//:: Modified By: Deva Winblood -//:: Modified On: Jan 4th, 2008 -//:: Added Support for Mounted Combat Feat Support -//::////////////////////////////////////////////// - -#include "nw_i0_generic" - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_default5 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnPhysicalAttacked event script; + Fires for all physical attacks, claws, weapons, fists, bow, etc. + Fires for taunt skill, animal empathy skill. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" void main() { - ExecuteScript("prc_npc_physatt", OBJECT_SELF); - - if (!GetLocalInt(GetModule(),"X3_NO_MOUNTED_COMBAT_FEAT")) - { // set variables on target for mounted combat - SetLocalInt(OBJECT_SELF,"bX3_LAST_ATTACK_PHYSICAL",TRUE); - SetLocalInt(OBJECT_SELF,"nX3_HP_BEFORE",GetCurrentHitPoints(OBJECT_SELF)); - } // set variables on target for mounted combat - - if(GetFleeToExit()) { - // Run away! - ActivateFleeToExit(); - } else if (GetSpawnInCondition(NW_FLAG_SET_WARNINGS)) { - // We give an attacker one warning before we attack - // This is not fully implemented yet - SetSpawnInCondition(NW_FLAG_SET_WARNINGS, FALSE); - - //Put a check in to see if this attacker was the last attacker - //Possibly change the GetNPCWarning function to make the check - } else { - object oAttacker = GetLastAttacker(); - if (!GetIsObjectValid(oAttacker)) { - // Don't do anything, invalid attacker - - } else if (!GetIsFighting(OBJECT_SELF)) { - // We're not fighting anyone else, so - // start fighting the attacker - if(GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) { - SetSummonHelpIfAttacked(); - DetermineSpecialBehavior(oAttacker); - } else if (GetArea(oAttacker) == GetArea(OBJECT_SELF)) { - SetSummonHelpIfAttacked(); - DetermineCombatRound(oAttacker); - } - - //Shout Attack my target, only works with the On Spawn In setup - SpeakString("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK); - - //Shout that I was attacked - SpeakString("NW_I_WAS_ATTACKED", TALKVOLUME_SILENT_TALK); - } + object oCreature = 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) + "."); + SetLocalObject(oAttacker, AI_ATTACKED_PHYSICAL, oCreature); + // Run away! + if(ai_GetFleeToExit(oCreature)) + { + ai_ActivateFleeToExit(oCreature); + return; } - - if(GetSpawnInCondition(NW_FLAG_ATTACK_EVENT)) { - SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_ATTACKED)); + SignalEvent(oCreature, EventUserDefined(EVENT_ATTACKED)); } + if(ai_GetIsBusy(oCreature) || ai_Disabled(oCreature)) return; + if(ai_GetIsInCombat(oCreature)) return; + // We only inform others if attacked when not busy, not disabled & not in combat. + if(AI_DEBUG) ai_Debug("nw_c2_default5", "30", "Tell my allies I've been attacked!"); + SetLocalObject (oCreature, AI_MY_TARGET, oAttacker); + SpeakString(AI_ATKED_BY_WEAPON, TALKVOLUME_SILENT_TALK); + // Now move towards the attack in the hopes we can see them. + if(GetDistanceBetween(oCreature, oAttacker) < AI_RANGE_CLOSE) ai_DoMonsterCombatRound(oCreature); + else ActionMoveToObject(oAttacker, TRUE, AI_RANGE_CLOSE); } diff --git a/_module/nss/nw_c2_default6.nss b/_module/nss/nw_c2_default6.nss index a2c22570..60aef49b 100644 --- a/_module/nss/nw_c2_default6.nss +++ b/_module/nss/nw_c2_default6.nss @@ -1,110 +1,34 @@ -//::////////////////////////////////////////////////// -//:: NW_C2_DEFAULT6 -//:: Default OnDamaged handler -/* - If already fighting then ignore, else determine - combat round - */ -//::////////////////////////////////////////////////// -//:: Copyright (c) 2002 Floodgate Entertainment -//:: Created By: Naomi Novik -//:: Created On: 12/22/2002 -//::////////////////////////////////////////////////// -//::////////////////////////////////////////////////// -//:: Modified By: Deva Winblood -//:: Modified On: Jan 17th, 2008 -//:: Added Support for Mounted Combat Feat Support -//::////////////////////////////////////////////////// - -#include "nw_i0_generic" -#include "x3_inc_horse" - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_default6 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnDamaged event script; + Does not fire if the creature dies from the damage. + Does not fire for plot creatures as they take no damage. + May fire before or after OnPhysicalAttacked event. + Fires when EffectDamage is applied to oCreature even if 0 damage. + Fires when a weapon damages a oCreature, but not if resisted. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" void main() { - - ExecuteScript("prc_npc_damaged", OBJECT_SELF); - - object oDamager = GetLastDamager(); - object oMe=OBJECT_SELF; - int nHPBefore; - if (!GetLocalInt(GetModule(),"X3_NO_MOUNTED_COMBAT_FEAT")) - if (GetHasFeat(FEAT_MOUNTED_COMBAT)&&HorseGetIsMounted(OBJECT_SELF)) - { // see if can negate some damage - if (GetLocalInt(OBJECT_SELF,"bX3_LAST_ATTACK_PHYSICAL")) - { // last attack was physical - nHPBefore=GetLocalInt(OBJECT_SELF,"nX3_HP_BEFORE"); - if (!GetLocalInt(OBJECT_SELF,"bX3_ALREADY_MOUNTED_COMBAT")) - { // haven't already had a chance to use this for the round - SetLocalInt(OBJECT_SELF,"bX3_ALREADY_MOUNTED_COMBAT",TRUE); - int nAttackRoll=GetBaseAttackBonus(oDamager)+d20(); - int nRideCheck=GetSkillRank(SKILL_RIDE,OBJECT_SELF)+d20(); - if (nRideCheck>=nAttackRoll&&!GetIsDead(OBJECT_SELF)) - { // averted attack - if (GetIsPC(oDamager)) SendMessageToPC(oDamager,GetName(OBJECT_SELF)+GetStringByStrRef(111991)); - //if (GetIsPC(OBJECT_SELF)) SendMessageToPCByStrRef(OBJECT_SELF,111992"); - if (GetCurrentHitPoints(OBJECT_SELF) (GetMaxHitPoints(OBJECT_SELF) / 4) - || (GetHitDice(oDamager) - 2) > GetHitDice(oTarget) - ) - ) - ) - { - // Switch targets - DetermineCombatRound(oDamager); - } - } - } - + object oCreature = OBJECT_SELF; + ExecuteScript("prc_npc_damaged", oCreature); // Send the user-defined event signal if(GetSpawnInCondition(NW_FLAG_DAMAGED_EVENT)) { - SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_DAMAGED)); + SignalEvent(oCreature, EventUserDefined(EVENT_DAMAGED)); + return; } + if(ai_Disabled(oCreature)) return; + // Make sure to clear wounded shout limit if we take damage. See ai_TryHealing. + DeleteLocalInt(oCreature, "AI_WOUNDED_SHOUT_LIMIT"); + object oDamager = GetLastDamager(oCreature); + if(AI_DEBUG) ai_Debug("nw_c2_default6", "23", GetName(oCreature) + " has been damaged by " + GetName(oDamager)); + if(ai_GetFleeToExit(oCreature)) return; + if(GetObjectType(oDamager) == OBJECT_TYPE_AREA_OF_EFFECT && + ai_IsInADangerousAOE(oCreature, AI_RANGE_BATTLEFIELD, TRUE)) return; + if(ai_GetIsBusy(oCreature) || ai_GetIsInCombat(oCreature)) return; + if(GetDistanceBetween(oCreature, oDamager) < AI_RANGE_CLOSE) ai_DoMonsterCombatRound(oCreature); + else ActionMoveToObject(oDamager, TRUE, AI_RANGE_CLOSE - 1.0); } diff --git a/_module/nss/nw_c2_default8.nss b/_module/nss/nw_c2_default8.nss index 731908d1..ef07c733 100644 --- a/_module/nss/nw_c2_default8.nss +++ b/_module/nss/nw_c2_default8.nss @@ -1,30 +1,26 @@ -//::////////////////////////////////////////////////// -//:: NW_C2_DEFAULT8 -/* - Default OnDisturbed event handler for NPCs. - */ -//::////////////////////////////////////////////////// -//:: Copyright (c) 2002 Floodgate Entertainment -//:: Created By: Naomi Novik -//:: Created On: 12/22/2002 -//::////////////////////////////////////////////////// - -#include "nw_i0_generic" - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_default8 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnDisturbed event script; + Fires when the inventory of oCreature is changed i.e. added or removed. + Creatures can't have items added or removed from its inventory (it's not a + container), then the only way this fires for creatures if something is stolen. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_actions" void main() { - ExecuteScript("prc_npc_disturb", OBJECT_SELF); - - object oTarget = GetLastDisturbed(); - - // If we've been disturbed and are not already fighting, - // attack our disturber. - if (GetIsObjectValid(oTarget) && !GetIsFighting(OBJECT_SELF)) { - DetermineCombatRound(oTarget); - } - + 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; + //object oTarget = GetLastDisturbed(); + //if (oTarget != OBJECT_INVALID) ai_DoMonsterCombatRound (); // Send the disturbed flag if appropriate. - if(GetSpawnInCondition(NW_FLAG_DISTURBED_EVENT)) { - SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_DISTURBED)); + if(GetSpawnInCondition(NW_FLAG_DISTURBED_EVENT)) + { + SignalEvent(oCreature, EventUserDefined(EVENT_DISTURBED)); } } diff --git a/_module/nss/nw_c2_defaultb.nss b/_module/nss/nw_c2_defaultb.nss index 13385446..050a3ea2 100644 --- a/_module/nss/nw_c2_defaultb.nss +++ b/_module/nss/nw_c2_defaultb.nss @@ -1,159 +1,42 @@ -//:://///////////////////////////////////////////// -//:: Default: On Spell Cast At -//:: NW_C2_DEFAULTB -//:: Copyright (c) 2001 Bioware Corp. -//::////////////////////////////////////////////// -/* - This determines if the spell just cast at the - target is harmful or not. - - GZ 2003-Oct-02 : - New AoE Behavior AI. Will use - Dispel Magic against AOES - - Flying Creatures will ignore - Grease - -*/ -//::////////////////////////////////////////////// -//:: Created By: Preston Watamaniuk -//:: Created On: Dec 6, 2001 -//:: Last Modified On: 2003-Oct-13 -//::////////////////////////////////////////////// -//::////////////////////////////////////////////// -//:: Modified By: Deva Winblood -//:: Modified On: Jan 4th, 2008 -//:: Added Support for Mounted Combat Feat Support -//::////////////////////////////////////////////// - -#include "nw_i0_generic" -#include "x2_i0_spells" - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_defaultb + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monster OnSpellCastAt event script; + Fires when oCreature becomes the target of a spell via SignalEvent. + Fires when a healing kit is used on a creature. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" void main() { - ExecuteScript("prc_npc_spellat", OBJECT_SELF); - + object oCreature = OBJECT_SELF; + ExecuteScript("prc_npc_spellat", oCreature); object oCaster = GetLastSpellCaster(); - - - if(GetLastSpellHarmful()) - { - SetCommandable(TRUE); - - if (!GetLocalInt(GetModule(),"X3_NO_MOUNTED_COMBAT_FEAT")) - { // set variables on target for mounted combat - DeleteLocalInt(OBJECT_SELF,"bX3_LAST_ATTACK_PHYSICAL"); - } // set variables on target for mounted combat - - // ------------------------------------------------------------------ - // If I was hurt by someone in my own faction - // Then clear any hostile feelings I have against them - // After all, we're all just trying to do our job here - // if we singe some eyebrow hair, oh well. - // ------------------------------------------------------------------ - if (GetFactionEqual(oCaster, OBJECT_SELF) == TRUE) - { - ClearPersonalReputation(oCaster, OBJECT_SELF); - ClearAllActions(TRUE); - DelayCommand(1.2, ActionDoCommand(DetermineCombatRound(OBJECT_INVALID))); - // Send the user-defined event as appropriate - if(GetSpawnInCondition(NW_FLAG_SPELL_CAST_AT_EVENT)) - { - SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_SPELL_CAST_AT)); - } - return; - } - - int bAttack = TRUE; - // ------------------------------------------------------------------ - // GZ, 2003-Oct-02 - // Try to do something smart if we are subject to an AoE Spell. - // ------------------------------------------------------------------ - if (MatchAreaOfEffectSpell(GetLastSpell()) == TRUE) - { - int nAI = (GetBestAOEBehavior(GetLastSpell())); // from x2_i0_spells - switch (nAI) - { - case X2_SPELL_AOEBEHAVIOR_DISPEL_L: - case X2_SPELL_AOEBEHAVIOR_DISPEL_N: - case X2_SPELL_AOEBEHAVIOR_DISPEL_M: - case X2_SPELL_AOEBEHAVIOR_DISPEL_G: - case X2_SPELL_AOEBEHAVIOR_DISPEL_C: - bAttack = FALSE; - ActionCastSpellAtLocation(nAI, GetLocation(OBJECT_SELF)); - ActionDoCommand(SetCommandable(TRUE)); - SetCommandable(FALSE); - break; - - case X2_SPELL_AOEBEHAVIOR_FLEE: - ClearActions(CLEAR_NW_C2_DEFAULTB_GUSTWIND); - oCaster = GetLastSpellCaster(); - ActionForceMoveToObject(oCaster, TRUE, 2.0); - DelayCommand(1.2, ActionDoCommand(DetermineCombatRound(oCaster))); - bAttack = FALSE; - break; - - case X2_SPELL_AOEBEHAVIOR_IGNORE: - // well ... nothing - break; - - case X2_SPELL_AOEBEHAVIOR_GUST: - ActionCastSpellAtLocation(SPELL_GUST_OF_WIND, GetLocation(OBJECT_SELF)); - ActionDoCommand(SetCommandable(TRUE)); - SetCommandable(FALSE); - bAttack = FALSE; - break; - } - - } - // --------------------------------------------------------------------- - // Not an area of effect spell, but another hostile spell. - // If we're not already fighting someone else, - // attack the caster. - // --------------------------------------------------------------------- - if( !GetIsFighting(OBJECT_SELF) && bAttack) - { - if(GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) - { - DetermineSpecialBehavior(oCaster); - } - else - { - DetermineCombatRound(oCaster); - } - } - - // We were attacked, so yell for help - SetCommandable(TRUE); - //Shout Attack my target, only works with the On Spawn In setup - SpeakString("NW_ATTACK_MY_TARGET", TALKVOLUME_SILENT_TALK); - - //Shout that I was attacked - SpeakString("NW_I_WAS_ATTACKED", TALKVOLUME_SILENT_TALK); - } - else - { - // --------------------------------------------------------------------- - // July 14, 2003 BK - // If there is a valid enemy nearby and a NON HARMFUL spell has been - // cast on me I should call DetermineCombatRound - // I may be invisible and casting spells on myself to buff myself up - // --------------------------------------------------------------------- - // Fix: JE - let's only do this if I'm currently in combat. If I'm not - // in combat, and something casts a spell on me, it'll make me search - // out the nearest enemy, no matter where they are on the level, which - // is kinda dumb. - object oEnemy =GetNearestEnemy(); - if ((GetIsObjectValid(oEnemy) == TRUE) && (GetIsInCombat() == TRUE)) - { - // SpeakString("keep me in combat"); - DetermineCombatRound(oEnemy); - } - } - + SetLocalObject(oCaster, AI_ATTACKED_SPELL, oCreature); + if(ai_Disabled(oCreature)) return; + if(!GetLastSpellHarmful()) return; // Send the user-defined event as appropriate if(GetSpawnInCondition(NW_FLAG_SPELL_CAST_AT_EVENT)) { SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_SPELL_CAST_AT)); } - - + // If the spell came from an ally, we don't want to hold it against them. + if(GetFactionEqual(oCaster, oCreature)) ClearPersonalReputation(oCaster, oCreature); + // Lets see what kind of area of effect this is and select an appropriate action. + int nSpell = GetLastSpell(); + if(AI_DEBUG) ai_Debug("nw_c2_defaultb", "26", GetName(oCreature) + " has been hit by a harmful spell(" + + Get2DAString("spells", "Label", nSpell) + ")!"); + if(ai_GetInAOEReaction(oCreature, oCaster, nSpell) && + ai_IsInADangerousAOE(oCreature, AI_RANGE_BATTLEFIELD, TRUE)) return; + if(ai_GetIsBusy(oCreature)) return; + if(ai_CheckForCombat(oCreature, TRUE)) return; + // We have been attacked out of combat, so let our allies know. + SetLocalObject(oCreature, AI_MY_TARGET, oCaster); + SpeakString(AI_ATKED_BY_SPELL, TALKVOLUME_SILENT_TALK); + if(GetDistanceBetween(oCreature, oCaster) < AI_RANGE_CLOSE) + { + if(ai_GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL)) ai_DetermineSpecialBehavior(oCreature); + else ai_DoMonsterCombatRound(oCreature); + } + else ActionMoveToObject(oCaster, TRUE, AI_RANGE_CLOSE); } diff --git a/_module/nss/nw_c2_defaulte.nss b/_module/nss/nw_c2_defaulte.nss index 3d8ab2c9..98e43646 100644 --- a/_module/nss/nw_c2_defaulte.nss +++ b/_module/nss/nw_c2_defaulte.nss @@ -1,51 +1,54 @@ -//:://///////////////////////////////////////////// -//:: Default On Blocked -//:: NW_C2_DEFAULTE -//:: Copyright (c) 2001 Bioware Corp. -//::////////////////////////////////////////////// -/* - This will cause blocked creatures to open - or smash down doors depending on int and - str. -*/ -//::////////////////////////////////////////////// -//:: Created By: Preston Watamaniuk -//:: Created On: Nov 23, 2001 -//::////////////////////////////////////////////// - +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_c2_defaulte + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Monsters OnBlocked event script; + Can be blocked by a creature or door. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" void main() { - ExecuteScript("prc_npc_blocked", OBJECT_SELF); - - object oDoor = GetBlockingDoor(); - if (GetObjectType(oDoor) == OBJECT_TYPE_CREATURE) + 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)); + int nObjectType = GetObjectType(oObject); + if(nObjectType == OBJECT_TYPE_CREATURE) { - // * Increment number of times blocked - /*SetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED", GetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED") + 1); - if (GetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED") > 3) + if(GetIsEnemy(oObject, oCreature)) { - SpeakString("Blocked by creature"); - SetLocalInt(OBJECT_SELF, "X2_NUMTIMES_BLOCKED",0); - ClearAllActions(); - object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY); - if (GetIsObjectValid(oEnemy) == TRUE) + if(ai_CanIAttack(oCreature) && ai_GetIsInCombat(oCreature)) { - ActionEquipMostDamagingRanged(oEnemy); - ActionAttack(oEnemy); + ai_DoMonsterCombatRound(oCreature); + return; } - return; - } */ + if(ai_CheckForCombat(oCreature, TRUE)) return; + } + } + // Anything below blocking us is a door. + if(nObjectType != OBJECT_TYPE_DOOR) return; + // Only open the door if the player has turned door opening on. + if(!GetLocalInt(GetModule(), AI_RULE_OPEN_DOORS)) return; + //if(GetLockKeyTag(oObject) != "") return; + else if(GetIsDoorActionPossible(oObject, DOOR_ACTION_OPEN) && + GetAbilityScore(oCreature, ABILITY_INTELLIGENCE) >= 5) + { + if(AI_DEBUG) ai_Debug("nw_c2_defaulte", "33", GetName(oCreature) + " is opening " + GetName(oObject)); + DoDoorAction(oObject, DOOR_ACTION_OPEN); return; } - if(GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) >= 5) + // If we are in combat we should ignore doors that do not easily open. + if(GetIsDoorActionPossible(oObject, DOOR_ACTION_BASH) && + ai_GetWeaponDamage(oCreature, 3, TRUE) > GetHardness(oObject) && + GetLockKeyTag(oObject) == "") { - if(GetIsDoorActionPossible(oDoor, DOOR_ACTION_OPEN) && GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) >= 7 ) - { - DoDoorAction(oDoor, DOOR_ACTION_OPEN); - } - else if(GetIsDoorActionPossible(oDoor, DOOR_ACTION_BASH)) - { - DoDoorAction(oDoor, DOOR_ACTION_BASH); - } + ActionWait(1.0); + ActionAttack(oObject); + // Give them 3 rounds to break through a door. + DelayCommand(18.0, ai_ClearCreatureActions(TRUE)); + return; } } + + diff --git a/_module/nss/nw_ch_ac1.nss b/_module/nss/nw_ch_ac1.nss new file mode 100644 index 00000000..6ed2fea5 --- /dev/null +++ b/_module/nss/nw_ch_ac1.nss @@ -0,0 +1,158 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_ac1 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associate(Summons, Familiar, Companion) OnHeart beat script when out of combat; + This will usually fire every 6 seconds (1 game round). +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_module" +#include "0i_menus" +void ai_ActionFollow(object oCreature, object oTarget) +{ + if(GetLocalInt(OBJECT_SELF, AI_CURRENT_ACTION_MODE) == AI_LAST_ACTION_MOVE) + { + float fDistance = GetDistanceBetween(oCreature, oTarget); + float fFollowDistance = ai_GetFollowDistance(oCreature); + if(fDistance > fFollowDistance) + { + if(fDistance > fFollowDistance * 5.0 && + ai_GetIsInCombat(oCreature)) AssignCommand(oCreature, JumpToObject(oTarget)); + else + { + ClearAllActions(); + ActionMoveToObject(oTarget, TRUE, fFollowDistance); + } + } + DelayCommand(1.0, ai_ActionFollow(oCreature, oTarget)); + } +} +void main() +{ + if (GetAILevel(OBJECT_SELF) == AI_LEVEL_VERY_LOW) return; + object oCreature = OBJECT_SELF; + if(AI_DEBUG) ai_Counter_Start(); + // We run our OnSpawn in the heartbeat so the creator can use the original + // OnSpawn for their own use. + ai_OnAssociateSpawn(oCreature); + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + ": Heartbeat, ai_OnAssociateSpawn"); + if(AI_DEBUG) ai_Debug("nw_ch_ac1", "37", GetName(oCreature) + " Heartbeat." + + " MODE_FOLLOW: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_FOLLOW)) + + " Action: " + IntToString(GetCurrentAction(oCreature))); + if(ai_GetIsBusy(oCreature) || ai_Disabled(oCreature)) return; + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + ": Heartbeat, ai_GetIsBusy/ai_Disabled"); + // If we are an associate and don't have a master then exit. + object oMaster = GetMaster(oCreature); + if(AI_DEBUG) ai_Debug("nw_ch_ac1", "43", "oMaster: " + GetName(oMaster)); + if(oMaster == OBJECT_INVALID) + { + if(ai_GetIsInCombat(oCreature)) + { + ai_DoAssociateCombatRound(oCreature); + return; + } + ai_CheckForCombat(oCreature, FALSE); + return; + } + // ***** Code for Henchman data and menus ***** + if(ai_GetIsCharacter(oMaster)) + { + string sAssociateType = ai_GetAssociateType(oMaster, oCreature); + ai_CheckAssociateData(oMaster, oCreature, sAssociateType); + ai_CheckPCStart(oMaster); + if(AI_HENCHMAN_WIDGET) + { + // This keeps widgets from disappearing and reappearing. + int nUiToken = NuiFindWindow(oMaster, sAssociateType + AI_WIDGET_NUI); + if(nUiToken) + { + json jData = NuiGetUserData(oMaster, nUiToken); + object oAssociate = StringToObject(JsonGetString(JsonArrayGet(jData, 0))); + if(oAssociate != oCreature) NuiDestroy(oMaster, nUiToken); + } + else + { + if(!ai_GetWidgetButton(oMaster, BTN_WIDGET_OFF, oCreature, sAssociateType)) + { + ai_CreateWidgetNUI(oMaster, oCreature); + } + } + } + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + ": Heartbeat, Get Associate data/Build widget"); + } + // If follow mode we do not want the NPC doing anything but follow. + if(!ai_GetAIMode(oCreature, AI_MODE_FOLLOW)) + { + if(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) + { + ai_TryHealing(oCreature, oCreature); + return; + } + if(ai_GetIsInCombat(oCreature)) + { + ai_DoAssociateCombatRound(oCreature); + return; + } + if(ai_CheckForCombat(oCreature, FALSE)) return; + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + ": Heartbeat, ai_CheckForCombat"); + if(IsInConversation(oCreature)) return; + // In command mode we let the player tell us what to do. + if(!ai_GetAIMode(oCreature, AI_MODE_COMMANDED)) + { + if(ai_TryHealing(oCreature, oCreature)) return; + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + ": Heartbeat: TryHealing"); + if(ai_CheckNearbyObjects(oCreature)) return; + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + ": Heartbeat: CheckNearbyObjects"); + if(ai_GetAIMode(oCreature, AI_MODE_SCOUT_AHEAD)) + { + ai_ScoutAhead(oCreature); + return; + } + } + } + // Finally we check to make sure we are following our master. + if(GetCurrentAction(oCreature) != ACTION_FOLLOW) + { + //ai_Debug("nw_ch_ac1", "66", "Follow master: " + + // " Stealth: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH)) + + // " Search: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH))); + if(ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH)) + { + if(AI_DEBUG) ai_Debug("nw_ch_ac1", "120", "Going into stealth mode!"); + int nStealth = GetSkillRank(SKILL_HIDE, oCreature); + nStealth += GetSkillRank(SKILL_MOVE_SILENTLY, oCreature); + if(nStealth / 2 >= ai_GetCharacterLevels(oCreature)) + { + SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE); + SetActionMode(oCreature, ACTION_MODE_DETECT, FALSE); + } + } + else + { + SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE); + if(ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH)) + { + if(AI_DEBUG) ai_Debug("nw_ch_ac1", "134", "Going into search mode!"); + SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE); + } + else SetActionMode(oCreature, ACTION_MODE_DETECT, FALSE); + } + // Follow master. + if(GetDistanceBetween(oCreature, oMaster) > ai_GetFollowDistance(oCreature)) + { + if(!ai_GetAIMode(oCreature, AI_MODE_COMMANDED)) + { + object oTarget = GetLocalObject(oCreature, AI_FOLLOW_TARGET); + if(oTarget == OBJECT_INVALID) oTarget = oMaster; + //ActionForceFollowObject(oTarget, ai_GetFollowDistance(oCreature)); + //ActionMoveToObject(oTarget, TRUE, ai_GetFollowDistance(oCreature)); + SetLocalInt(oCreature, AI_CURRENT_ACTION_MODE, AI_LAST_ACTION_MOVE); + ai_ActionFollow(oCreature, oTarget); + } + } + } + if(AI_DEBUG) ai_Counter_End(GetName(oCreature) + ": Heartbeat, end"); + if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT)) + { + SignalEvent(OBJECT_SELF, EventUserDefined(1001)); + } +} diff --git a/_module/nss/nw_ch_ac2.nss b/_module/nss/nw_ch_ac2.nss new file mode 100644 index 00000000..04a56f98 --- /dev/null +++ b/_module/nss/nw_ch_ac2.nss @@ -0,0 +1,107 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_ac2 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associate(Summons, Familiars, Companions) OnPerception script when not in combat; + There are 4 types of perception - Heard, Inaudible, Seen, Vanished. + Only one type will ever be true in an event trigger. + The order of trigger is Heard/Seen and Inaudible/Vanished. + There are two states of percepion Heard and Seen. + These states can be set at the same time thus a heard event can see the creature. + Fires when ever one of these states changes from TRUE to FALSE or FALSE to TRUE. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + object oLastPerceived = GetLastPerceived(); + if(AI_DEBUG) + { + if(GetLastPerceptionHeard ()) + { + ai_Debug("nw_ch_ac2", "21", GetName(oCreature) + " heard " + + GetName(GetLastPerceived()) + " Distance: " + + FloatToString(GetDistanceBetween(GetLastPerceived(), oCreature), 0, 2) + + " Seen: " + IntToString(GetObjectSeen(oLastPerceived, oCreature)) + "."); + } + if(GetLastPerceptionSeen ()) + { + ai_Debug("nw_ch_ac2", "29", GetName(oCreature) + " sees " + + GetName(GetLastPerceived()) + " Distance: " + + FloatToString(GetDistanceBetween(GetLastPerceived(), oCreature), 0, 2) + "."); + } + if(GetLastPerceptionVanished ()) + { + ai_Debug("nw_ch_ac2", "35", GetName(oCreature) + " lost sight of " + + GetName(GetLastPerceived()) + "."); + } + } + // We do nothing on Inaudibles so drop out early! + if(GetLastPerceptionInaudible()) + { + ai_Debug("nw_ch_ac2", "42", GetName(oCreature) + " lost sound of " + + GetName(GetLastPerceived()) + "."); + return; + } + if(AI_DEBUG) ai_Debug("nw_ch_ac2", "46", "Dead? " + IntToString(GetIsDead(oLastPerceived)) + + " Enemy? " + IntToString(GetIsEnemy(oLastPerceived, oCreature))); + if(ai_Disabled(oCreature)) return; + if(GetIsDead(oLastPerceived) || !GetIsEnemy(oLastPerceived, oCreature)) return; + // All code below assumes the perceived creature is an enemy and is alive! + // **************************** ENEMY HEARD ******************************** + if(GetLastPerceptionHeard()) + { + // Since Heard is run before Seen, but the values are set at the same + // time we can skip heard checks on heard & seen creatures! + if(GetObjectSeen(oLastPerceived, oCreature)) + { + // If the creature we are perceiving was our invisible creature then + // remove that they are invisible. + if(oLastPerceived == GetLocalObject(oCreature, AI_IS_INVISIBLE)) + { + DeleteLocalObject(oCreature, AI_IS_INVISIBLE); + } + ai_AssociateEvaluateNewThreat(oCreature, oLastPerceived, AI_I_SEE_AN_ENEMY); + } + else ai_AssociateEvaluateNewThreat(oCreature, oLastPerceived, AI_I_HEARD_AN_ENEMY); + return; + } + // **************************** ENEMY SEEN ********************************* + if(GetLastPerceptionSeen()) + { + // If the creature we are perceiving was our invisible creature then + // remove that they are invisible. + if(oLastPerceived == GetLocalObject(oCreature, AI_IS_INVISIBLE)) + { + DeleteLocalObject(oCreature, AI_IS_INVISIBLE); + } + ai_AssociateEvaluateNewThreat(oCreature, oLastPerceived, AI_I_SEE_AN_ENEMY); + return; + } + // **************************** ENEMY VANISHED ***************************** + if(GetLastPerceptionVanished()) + { + // Lets keep a mental note of the invisible creature. + SetLocalObject(oCreature, AI_IS_INVISIBLE, oLastPerceived); + if(AI_DEBUG) ai_Debug("nw_ch_ac2", "86", " We saw " + GetName(oLastPerceived) + " disappear!"); + if(ai_GetIsBusy(oCreature)) return; + // If in combat check to see if our target disappeared. + // If they have and we are not in melee with them then reevaluate combat + // since we lost our target. + if(ai_GetIsInCombat(oCreature)) + { + if(AI_DEBUG) ai_Debug("nw_ch_ac2", "93", "Is this our target? " + + IntToString(ai_GetAttackedTarget(oCreature, TRUE, TRUE) == oLastPerceived)); + if(ai_GetAttackedTarget(oCreature, TRUE, TRUE) == oLastPerceived) + { + ai_DoAssociateCombatRound(oCreature); + } + return; + } + // If they are not invisible then that means they left our perception + // range and we need follow them. + if(ai_CanIAttack(oCreature)) ActionMoveToObject(oLastPerceived, TRUE, AI_RANGE_CLOSE); + } + // **************************** ENEMY INAUDIBLE***************************** + // Not used. +} diff --git a/_module/nss/nw_ch_ac3.nss b/_module/nss/nw_ch_ac3.nss new file mode 100644 index 00000000..9eb34065 --- /dev/null +++ b/_module/nss/nw_ch_ac3.nss @@ -0,0 +1,56 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_ac3 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associate (Summons, Familiars, Companions) OnCombatRoundEnd event script; + Fires at the end of each combat round (6 seconds). + This will fire as long as oCreature is in combat (GetIsInCombat()). + This event starts counting once a combat action is started. + Every time a spell is cast it will queue another end combat round so haste with + two spells cast will fire this twice in one round. + It will also fire at the end of a hostile effect that stops actions i.e Stunned, Knockdown etc. + Action modes are also cleared prior to this event executing! + GetAttemptedAttackTarget() & GetAttemptedSpellTarget() also get cleared prior to this event. + This event can be canceled with ClearAllActions(TRUE) and SurrenderToEnemies. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + if(AI_DEBUG) ai_Debug("nw_ch_ac3", "20", GetName(oCreature) + " ends combat round."); + if(ai_Disabled(oCreature)) return; + // Action modes get cleared prior to each OnCombatRoundEnd! + // We do this to keep the action mode going. + int nActionMode = GetLocalInt(oCreature, AI_CURRENT_ACTION_MODE); + if(nActionMode > 0) + { + SetActionMode(oCreature, nActionMode, TRUE); + // We don't want to use up all of the Dwarven Defenders uses! + if(nActionMode == 12) IncrementRemainingFeatUses(oCreature, FEAT_DWARVEN_DEFENDER_DEFENSIVE_STANCE); + } + int nAction = GetCurrentAction(oCreature); + if(AI_DEBUG) ai_Debug("nw_ch_ac3", "32", "nAction: " + IntToString(nAction)); + switch(nAction) + { + // These actions are uninteruptable. + case ACTION_MOVETOPOINT : + case ACTION_CASTSPELL : + case ACTION_ITEMCASTSPELL : + case ACTION_COUNTERSPELL : return; + // Might be doing a special action that is not a defined action. + case ACTION_INVALID : + { + int nCombatWait = GetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + if(AI_DEBUG) ai_Debug("nw_ch_ac3", "44", "nCombatWait: " + IntToString(nCombatWait)); + if(nCombatWait) + { + if(ai_IsInCombatRound(oCreature, nCombatWait)) return; + DeleteLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS); + } + } + // We always want to interupt an attack action at the end of a round. + //case ACTION_ATTACKOBJECT : + } + if(ai_GetIsInCombat(oCreature)) ai_DoAssociateCombatRound (oCreature); +} + diff --git a/_module/nss/nw_ch_ac4.nss b/_module/nss/nw_ch_ac4.nss new file mode 100644 index 00000000..f6c290d6 --- /dev/null +++ b/_module/nss/nw_ch_ac4.nss @@ -0,0 +1,45 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_ac4 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associate(Summons, Familiar, Companion) OnDialoge event script; + Fires when oCreature has been clicked on for conversation. + Fires when oCreature hears a shout from another creature. + If SetListening is FALSE then oCreature will not "hear" anything. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +#include "nw_inc_gff" +void main() +{ + object oCreature = OBJECT_SELF; + int nMatch = GetListenPatternNumber(); + if(AI_DEBUG) ai_Debug("nw_ch_ac4", "16", GetName(oCreature) + " listens " + + IntToString(nMatch) + " to " + GetName(GetLastSpeaker()) + "."); + // Skip ASSOCIATE_COMMAND_MASTERUNDERATTACK(11) since it fires for + // every physical attack made on our master. This fires alot! + if(nMatch == ASSOCIATE_COMMAND_MASTERUNDERATTACK) return; + // If we are disabled then we can't listen or talk, Busy is checked in ai_SelectAssociateCommand(). + if(ai_Disabled(oCreature)) return; + object oLastSpeaker = GetLastSpeaker(); + // Some commands override being busy so we check in ai_SelectAssociateCommand. + if(nMatch != -1) + { + if(GetFactionEqual(oLastSpeaker, oCreature)) ai_SelectAssociateCommand(oCreature, oLastSpeaker, nMatch); + } + else + { + if (!ai_GetIsBusy(oCreature)) + { + ai_ClearCreatureActions(); + if(GetAssociateType(oCreature) == ASSOCIATE_TYPE_HENCHMAN) BeginConversation("oc_ai_henchmen", oLastSpeaker); + else + { + json jHenchman = ObjectToJson(oCreature); + string sConversation = JsonGetString(GffGetResRef(jHenchman, "Conversation")); + if(sConversation == "") BeginConversation("oc_ai_henchmen", oLastSpeaker); + BeginConversation(); + } + } + } +} + diff --git a/_module/nss/nw_ch_ac5.nss b/_module/nss/nw_ch_ac5.nss new file mode 100644 index 00000000..78f93214 --- /dev/null +++ b/_module/nss/nw_ch_ac5.nss @@ -0,0 +1,51 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_ac5 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associates (Summons, Familiars, Companions) OnPhysicalAttacked event script; + Fires for all physical attacks, claws, weapons, fists, bow, etc. + Fires for taunt skill, animal empathy skill. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + object oAttacker = GetLastAttacker(); + if(AI_DEBUG) ai_Debug("nw_ch_ac5", "14", GetName(oCreature) + " was attacked by " + + GetName(oAttacker) + "."); + SetLocalObject(oAttacker, AI_ATTACKED_PHYSICAL, oCreature); + if(ai_GetIsBusy(oCreature) || ai_Disabled(oCreature)) return; + if(GetSpawnInCondition(NW_FLAG_ATTACK_EVENT)) + { + SignalEvent(OBJECT_SELF, EventUserDefined(1005)); + } + if(ai_GetIsInCombat(oCreature)) return; + // We only inform others if attacked when not busy, not disabled, & not in combat. + SetLocalObject(oCreature, AI_MY_TARGET, oAttacker); + SpeakString(AI_ATKED_BY_WEAPON, TALKVOLUME_SILENT_TALK); + // If they are using a melee weapon then make sure we are using our perception range. + // Don't go running towards them just yet, but if its a ranged weapon then react. + if(ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oAttacker))) + { + float fDistance = GetDistanceBetween(oCreature, oAttacker); + float fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE); + if(fDistance > fPerceptionDistance) return; + } + int nAction = GetCurrentAction(oCreature); + float fDistance = GetDistanceBetween(oCreature, oAttacker); + if(!ai_CanIAttack(oCreature)) + { + // We should defend ourselves if we are in Hold mode. + if(!ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) return; + // Only defend against melee attacks. + if(fDistance > AI_RANGE_MELEE) return; + } + // The only way to get here is to not be in combat. + if(fDistance < AI_RANGE_CLOSE) + { + ai_StartAssociateCombat(oCreature); + } + else ActionMoveToObject(oAttacker, TRUE, AI_RANGE_CLOSE - 1.0); +} + + diff --git a/_module/nss/nw_ch_ac6.nss b/_module/nss/nw_ch_ac6.nss new file mode 100644 index 00000000..f51e9374 --- /dev/null +++ b/_module/nss/nw_ch_ac6.nss @@ -0,0 +1,32 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_ch_6_damaged + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Player OnDamaged script for PC AI; + Does not fire if the creature dies from the damage. + Does not fire for plot creatures as they take no damage. + May fire before or after OnPhysicalAttacked event. + Fires when EffectDamage is applied to oCreature even if 0 damage. + Fires when a weapon damages a oCreature, but not if resisted. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + if(ai_Disabled(oCreature)) return; + // Make sure to clear wounded shout limit if we take damage. See ai_TryHealing. + DeleteLocalInt(oCreature, "AI_WOUNDED_SHOUT_LIMIT"); + object oDamager = GetLastDamager(oCreature); + if(AI_DEBUG) ai_Debug("nw_ch_ac6", "18", GetName(oCreature) + " has been damaged by " + GetName(oDamager)); + if(GetSpawnInCondition(NW_FLAG_DAMAGED_EVENT)) + { + SignalEvent(OBJECT_SELF, EventUserDefined(1006)); + } + if(GetObjectType(oDamager) == OBJECT_TYPE_AREA_OF_EFFECT && + ai_IsInADangerousAOE(oCreature, AI_RANGE_BATTLEFIELD, TRUE)) return; + if(ai_GetIsBusy(oCreature) || ai_GetIsInCombat(oCreature)) return; + if(!ai_CanIAttack(oCreature)) return; + if(GetDistanceBetween(oCreature, oDamager) < AI_RANGE_CLOSE) ai_DoAssociateCombatRound(oCreature); + else ActionMoveToObject(oDamager, TRUE, AI_RANGE_CLOSE - 1.0); +} + diff --git a/_module/nss/nw_ch_ac8.nss b/_module/nss/nw_ch_ac8.nss new file mode 100644 index 00000000..05b7f856 --- /dev/null +++ b/_module/nss/nw_ch_ac8.nss @@ -0,0 +1,25 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_ac8 + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associates (Summons, Familiars, Companions) OnDisturbed event script. + Fires when the inventory of oCreature is changed i.e. added or removed. + Creatures can't have items added or removed from its inventory (it's not a + container), then the only way this fires for creatures if something is stolen. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + if(AI_DEBUG) ai_Debug("nw_ch_ac8", "13", GetName(OBJECT_SELF) + " is been disturbed!"); + if(GetSpawnInCondition(NW_FLAG_DISTURBED_EVENT)) + { + SignalEvent(OBJECT_SELF, EventUserDefined(1008)); + } + // 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; + //object oTarget = GetLastDisturbed(); + //if (oTarget != OBJECT_INVALID) ai_DoMonsterCombatRound (); +} + + diff --git a/_module/nss/nw_ch_aca.nss b/_module/nss/nw_ch_aca.nss new file mode 100644 index 00000000..99b6d5fa --- /dev/null +++ b/_module/nss/nw_ch_aca.nss @@ -0,0 +1,46 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_aca + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associate OnRested event script; + Fires when the creature attempts to rest via ActionRest or a PC rests. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_menus" +void ai_UpdateAssociateWidget(object oMaster, object oAssociate, int nUIToken) +{ + if(nUIToken) NuiDestroy(oMaster, nUIToken); + ai_CreateWidgetNUI(oMaster, oAssociate); + if(oMaster != oAssociate) + { + nUIToken = NuiFindWindow(oMaster, "pc" + AI_WIDGET_NUI); + if(nUIToken) + { + NuiDestroy(oMaster, nUIToken); + ai_CreateWidgetNUI(oMaster, oMaster); + } + } +} +void main() +{ + object oAssociate = OBJECT_SELF; + ai_ClearCreatureActions(); + ai_OnRested(oAssociate); + object oMaster = GetMaster(oAssociate); + if(ai_GetIsCharacter(oMaster) && AI_HENCHMAN_WIDGET) + { + int nLevel = ai_GetCharacterLevels(oAssociate); + float fDelay = StringToFloat(Get2DAString("restduration", "DURATION", nLevel)); + fDelay = (fDelay / 1000.0f) + 6.0f; + // Update widget for spell widget. + string sAssociateType = ai_GetAssociateType(oMaster, oAssociate); + int nUIToken = NuiFindWindow(oMaster, sAssociateType + AI_WIDGET_NUI); + if(nUIToken) DelayCommand(fDelay, ai_UpdateAssociateWidget(oMaster, oAssociate, nUIToken)); + else + { + if(!ai_GetWidgetButton(oMaster, BTN_WIDGET_OFF, oAssociate, sAssociateType)) + { + DelayCommand(fDelay, ai_UpdateAssociateWidget(oMaster, oAssociate, 0)); + } + } + } +} diff --git a/_module/nss/nw_ch_acb.nss b/_module/nss/nw_ch_acb.nss new file mode 100644 index 00000000..ec96e77f --- /dev/null +++ b/_module/nss/nw_ch_acb.nss @@ -0,0 +1,42 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: nw_ch_acb + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associates (Summons, Familiars, Companions) OnSpellCastAt event script; + Fires when oCreature becomes the target of a spell via SignalEvent. + Fires when a healing kit is used on a creature. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + object oCaster = GetLastSpellCaster(); + SetLocalObject(oCaster, AI_ATTACKED_SPELL, oCreature); + if(ai_Disabled(oCreature)) return; + if(!GetLastSpellHarmful()) return; + // If the spell came from an ally, we don't want to hold it against them. + if(GetFactionEqual(oCaster, oCreature)) + { + ClearPersonalReputation(oCaster, oCreature); + if(GetSpawnInCondition(NW_FLAG_SPELL_CAST_AT_EVENT)) + { + SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_SPELL_CAST_AT)); + } + } + // Lets see what kind of area of effect this is and select an appropriate action. + 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) && + ai_IsInADangerousAOE(oCreature, AI_RANGE_BATTLEFIELD, TRUE)) return; + if(ai_GetIsBusy(oCreature)) return; + if(ai_CheckForCombat(oCreature, FALSE)) return; + // We were attacked by an enemy out of combat, so let our allies know. + SetLocalObject(oCreature, AI_MY_TARGET, oCaster); + SpeakString(AI_ATKED_BY_SPELL, TALKVOLUME_SILENT_TALK); + if(!ai_CanIAttack(oCreature)) return; + if(GetDistanceBetween(oCreature, oCaster) < AI_RANGE_CLOSE) ai_DoAssociateCombatRound(oCreature); + else ActionMoveToObject(oCaster, TRUE, AI_RANGE_CLOSE - 1.0); +} + + diff --git a/_module/nss/nw_ch_ace.nss b/_module/nss/nw_ch_ace.nss new file mode 100644 index 00000000..688ab902 --- /dev/null +++ b/_module/nss/nw_ch_ace.nss @@ -0,0 +1,60 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: 0e_ch_e_blocked + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Associates OnBlocked event script; + Can be blocked by a creature or door. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_associates" +void main() +{ + object oCreature = OBJECT_SELF; + // This actually gets either a Creature or Door that is blocking OBJECT_SELF. + object oObject = GetBlockingDoor(); + if(AI_DEBUG) ai_Debug("nw_ch_ace", "14", GetName(oCreature) + " is being blocked by " + GetName(oObject)); + int nObjectType = GetObjectType(oObject); + if(nObjectType == OBJECT_TYPE_CREATURE) + { + if(GetIsEnemy(oObject, oCreature)) + { + if(ai_CanIAttack(oCreature) && ai_GetIsInCombat(oCreature)) + { + ai_DoAssociateCombatRound(oCreature); + return; + } + if(ai_CheckForCombat(oCreature, FALSE)) return; + } + } + // Anything below blocking us is a door. + if(nObjectType != OBJECT_TYPE_DOOR) return; + if(!ai_GetAIMode(oCreature, AI_MODE_OPEN_DOORS)) return; + //if(GetLockKeyTag(oObject) != "") return; + else if(GetIsDoorActionPossible(oObject, DOOR_ACTION_OPEN) && + GetAbilityScore(oCreature, ABILITY_INTELLIGENCE) >= 5) + { + DoDoorAction(oObject, DOOR_ACTION_OPEN); + return; + } + // Anything below is ignored in combat. + if(ai_GetIsInCombat(oCreature)) return; + if(GetIsDoorActionPossible(oObject, DOOR_ACTION_BASH) && + ai_GetWeaponDamage(oCreature, 3, TRUE) > GetHardness(oObject) && + GetLockKeyTag(oObject) == "") + { + ActionWait(1.0); + ActionAttack(oObject); + // Give them 3 rounds to break through a door. + DelayCommand(18.0, ai_ClearCreatureActions(TRUE)); + return; + } + else if(GetLocked(oObject)) + { + if(AI_DEBUG) ai_Debug("nw_ch_ace", "49", GetName(oObject) + " is locked!"); + ai_AttemptToByPassLock(oCreature, oObject); + } + // Clear our action so we can move on to something else unless the door is open. + else if(!GetIsOpen(oObject)) + { + ai_ClearCreatureActions(); + } +} diff --git a/_module/nss/nw_ch_summon_9.nss b/_module/nss/nw_ch_summon_9.nss new file mode 100644 index 00000000..ca5a87c4 --- /dev/null +++ b/_module/nss/nw_ch_summon_9.nss @@ -0,0 +1,40 @@ +//:://///////////////////////////////////////////// +//:: Associate: On Spawn In +//:: nw_ch_summon_9 +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + +This must support the OC henchmen and all summoned/companion +creatures. + +*/ +//::////////////////////////////////////////////// +//:: Created By: Preston Watamaniuk +//:: Created On: Nov 19, 2001 +//::////////////////////////////////////////////// +//:: Updated By: Georg Zoeller, 2003-08-20: Added variable check for spawn in animation +#include "X0_INC_HENAI" +#include "x2_inc_switches" +void main() +{ + //Sets up the special henchmen listening patterns + SetAssociateListenPatterns(); + + // Set additional henchman listening patterns + //bkSetListeningPatterns(); + // * If Incorporeal, apply changes + if (GetCreatureFlag(OBJECT_SELF, CREATURE_VAR_IS_INCORPOREAL) == TRUE) + { + effect eConceal = EffectConcealment(50, MISS_CHANCE_TYPE_NORMAL); + eConceal = ExtraordinaryEffect(eConceal); + effect eGhost = EffectCutsceneGhost(); + eGhost = ExtraordinaryEffect(eGhost); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eConceal, OBJECT_SELF); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, OBJECT_SELF); + } + // Set starting location + SetAssociateStartLocation(); +} + + diff --git a/_module/nss/pc_savebuffs.nss b/_module/nss/pc_savebuffs.nss new file mode 100644 index 00000000..96b42d00 --- /dev/null +++ b/_module/nss/pc_savebuffs.nss @@ -0,0 +1,182 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pc_savebuffs +//////////////////////////////////////////////////////////////////////////////// + Used with pi_buffing to run the buffing plugin for + Philos Single Player Enhancements. + +Note: If a spell saves incorrectly check the spell script to see if the correct +spell is being passed through the SignalEvent correctly. +Known error in Shield of Faith spell as the below code in the shield of faith +script sends Camoflage instead! +"SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, 421, FALSE));" +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +string GetBuffDatabaseString(object oPlayer, string sDataField, string sTag = ""); +// sDataField should be one of the data fields for that table. +// sData is the string data to be saved. +void SetBuffDatabaseString(object oPlayer, string sDataField, string sData, string sTag = ""); +// sDataField should be one of the data fields for that table. +// jData is the json data to be saved. +void SetBuffDatabaseJson(object oPlayer, string sDataField, json jData, string sTag = ""); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +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) + { + string sScript = GetLocalString(oTarget, "AI_BUFF_CAST_AT_SCRIPT"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, sScript); + 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(); + string sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + if(nDomain) sName += " [Domain]"; + if(nMetaMagic > 0 && StringToInt(Get2DAString("classes", "MemorizesSpells", nClass))) + { + // We must add the level of the metamagic to the spells level to get the spells correct level. + if(nMetaMagic == METAMAGIC_EMPOWER) { sName += " (Empowered)"; nLevel += 2; } + else if(nMetaMagic == METAMAGIC_EXTEND) { sName += " (Extended)"; nLevel += 1; } + else if(nMetaMagic == METAMAGIC_MAXIMIZE) { sName += " (Maximized)"; nLevel += 3; } + else if(nMetaMagic == METAMAGIC_QUICKEN) { sName += " (Quickened)"; nLevel += 4; } + else if(nMetaMagic == METAMAGIC_SILENT) { sName += " (Silent)"; nLevel += 1; } + else if(nMetaMagic == METAMAGIC_STILL) { sName += " (Still)"; nLevel += 1; } + } + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + string sList = JsonGetString(JsonArrayGet(jMenuData, 0)); + json jSpells = GetBuffDatabaseJson(oPC, "spells", sList); + json jSpell = JsonArray(); + jSpell = JsonArrayInsert(jSpell, JsonInt(nSpell)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nClass)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nLevel)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nMetaMagic)); + jSpell = JsonArrayInsert(jSpell, JsonInt(nDomain)); + string sTargetName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oTarget, TRUE))); + jSpell = JsonArrayInsert(jSpell, JsonString(sTargetName)); + jSpell = JsonArrayInsert(jSpells, jSpell); + SetBuffDatabaseJson(oPC, "spells", jSpells, sList); + SendMessageToPC(oPC, sName + " has been saved for fast buffing on " + sTargetName + "."); + ExecuteScript("pi_buffing", oPC); +} +string GetBuffDatabaseString(object oPlayer, string sDataField, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "SELECT " + sDataField + " FROM BUFF_TABLE WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + if (SqlStep (sql)) return SqlGetString (sql, 0); + else return ""; +} +void SetBuffDatabaseString(object oPlayer, string sDataField, string sData, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "UPDATE BUFF_TABLE SET " + sDataField + " = @data WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@data", sData); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + SqlStep (sql); +} +void SetBuffDatabaseJson (object oPlayer, string sDataField, json jData, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "UPDATE BUFF_TABLE SET " + sDataField + " = @data WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindJson (sql, "@data", jData); + SqlBindString (sql, "@name", sName); + SqlBindString (sql, "@tag", sTag); + SqlStep (sql); +} +json GetBuffDatabaseJson (object oPlayer, string sDataField, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "SELECT " + sDataField + " FROM BUFF_TABLE WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString (sql, "@name", sName); + SqlBindString (sql, "@tag", sTag); + if (SqlStep (sql)) return SqlGetJson (sql, 0); + else return JsonArray (); +} +int GetHasDomainSpell(object oCaster, int nClass, int nLevel, int nSpell) +{ + int nIndex, nMaxIndex, nMSpell, nMmSpell, bDomain, nSubRadSpell, nSubSpell; + string sSubRadSpell; + if(StringToInt(Get2DAString("classes", "MemorizesSpells", nClass))) + { + nMaxIndex = GetMemorizedSpellCountByLevel(oCaster, nClass, nLevel); + while(nIndex < nMaxIndex) + { + nMSpell = GetMemorizedSpellId(oCaster, nClass, nLevel, nIndex); + if(nSpell == nMSpell) + { + if(GetMemorizedSpellIsDomainSpell(oCaster, nClass, nLevel, nIndex)) return nLevel; + } + nIndex ++; + } + } + return 0; +} diff --git a/_module/nss/pe_buffing.nss b/_module/nss/pe_buffing.nss new file mode 100644 index 00000000..a6c40500 --- /dev/null +++ b/_module/nss/pe_buffing.nss @@ -0,0 +1,534 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pe_buffing +//////////////////////////////////////////////////////////////////////////////// + Used with pi_buffing to run the buffing plugin for + Philos Single Player Enhancements. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" + +const int BUFF_MAX_SPELLS = 50; +const string FB_NO_MONSTER_CHECK = "FB_NO_MONSTER_CHECK"; + +// sDataField should be one of the data fields for that table. +// sData is the string data to be saved. +void SetBuffDatabaseString(object oPlayer, string sDataField, string sData, string sTag); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +string GetBuffDatabaseString(object oPlayer, string sDataField, string sTag); +// sDataField should be one of the data fields for that table. +// jData is the json data to be saved. +void SetBuffDatabaseJson(object oPlayer, string sDataField, json jData, string sTag); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +json GetBuffDatabaseJson(object oPlayer, string sDataField, string sTag); +// Casts all buff spells saved to the widget button. +void CastSavedBuffSpells(object oPC); +// Will check and make sure the spell is memorized and/or ready. +// Returns TRUE if memorized and ready, FALSE if memorized but not ready, +// and -1 if not memorized for classes that memorize. +// nSpell is the spell to find. +// nClass that cast the spell. +// nLevel the level of the spell. +// nMetamagic is if it has metamagic on it. +// nDomain is if it is a domain spell. +int GetSpellReady(object oCaster, int nSpell, int nClass, int nLevel, int nMetamagic, int nDomain); +// Creates the Buffing widget. +void PopupWidgetBuffGUIPanel(object oPC); +void main() +{ + object oPC = NuiGetEventPlayer(); + int nToken = NuiGetEventWindow(); + string sEvent = NuiGetEventType(); + string sElem = NuiGetEventElement(); + string sWndId = NuiGetWindowId (oPC, nToken); + //************************************************************************** + // Watch to see if the window moves and save. + if(sElem == "window_geometry" && sEvent == "watch") + { + if(!GetLocalInt (oPC, AI_NO_NUI_SAVE)) + { + // 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"); + } + return; + } + //************************************************************************** + // Spell Buffing. + if(sWndId == "plbuffwin") + { + if(sEvent == "click") + { + string sList; + if(GetStringLeft(sElem, 10) == "btn_spell_") + { + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + sList = JsonGetString(JsonArrayGet(jMenuData, 0)); + json jSpells = GetBuffDatabaseJson(oPC, "spells", sList); + int nIndex = StringToInt(GetStringRight(sElem, GetStringLength(sElem) - 10)); + int nSpell = JsonGetInt(JsonArrayGet(JsonArrayGet(jSpells, nIndex), 0)); + string sName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + jSpells = JsonArrayDel(jSpells, nIndex); + SetBuffDatabaseJson(oPC, "spells", jSpells, sList); + ai_SendMessages(sName + " has been removed from the list.", AI_COLOR_YELLOW, oPC); + ExecuteScript("pi_buffing", oPC); + } + else if(sElem == "btn_save") + { + string sScript; + object oCreature; + if(JsonGetInt(NuiGetBind (oPC, nToken, "btn_save"))) + { + sScript = GetEventScript(oPC, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT); + SetLocalObject(oPC, "AI_BUFF_PC", oPC); + SetLocalString(oPC, "AI_BUFF_CAST_AT_SCRIPT", sScript); + SetEventScript(oPC, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "pc_savebuffs"); + // Setup your followers to allow spells to be saved on them as well. + int nAssociateType = 2; + object oAssociate = GetAssociate(nAssociateType, oPC); + while(nAssociateType < 5) + { + if(oAssociate != OBJECT_INVALID) + { + SetLocalString(oAssociate, "AI_BUFF_CAST_AT_SCRIPT", sScript); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "pc_savebuffs"); + } + oAssociate = GetAssociate(++nAssociateType, oPC); + } + int nIndex = 1; + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + while(nIndex <= AI_MAX_HENCHMAN) + { + if(oAssociate != OBJECT_INVALID) + { + SetLocalString(oAssociate, "AI_BUFF_CAST_AT_SCRIPT", sScript); + SetEventScript(oAssociate, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "pc_savebuffs"); + } + oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, ++nIndex); + } + ai_SendMessages("Cast spells on yourself or an associate to save them to the widget.", AI_COLOR_YELLOW, oPC); + } + else + { + DeleteLocalObject(oPC, "AI_BUFF_PC"); + 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, nToken, "btn_save", JsonBool(FALSE)); + ai_SendMessages("Saving spells to the list has been turned off.", AI_COLOR_YELLOW, oPC); + } + } + else if(sElem == "btn_clear") + { + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + sList = JsonGetString(JsonArrayGet(jMenuData, 0)); + SetBuffDatabaseJson(oPC, "spells", JsonArray(), sList); + ExecuteScript("pi_buffing", oPC); + } + else if(sElem == "btn_buff") CastSavedBuffSpells(oPC); + // Runs all the List 1-4 buttons. + if(GetStringLeft(sElem, 8) == "btn_list") + { + sList = "list" + GetStringRight(sElem, 1); + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + jMenuData = JsonArraySet(jMenuData, 0, JsonString(sList)); + SetBuffDatabaseJson(oPC, "spells", jMenuData, "menudata"); + ExecuteScript("pi_buffing", oPC); + } + } + else if(sEvent == "watch") + { + if(sElem == "buff_widget_check") + { + int bBuffWidget = JsonGetInt(NuiGetBind(oPC, nToken, "buff_widget_check")); + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + jMenuData = JsonArraySet(jMenuData, 3, JsonBool(bBuffWidget)); + SetBuffDatabaseJson(oPC, "spells", jMenuData, "menudata"); + if(bBuffWidget) PopupWidgetBuffGUIPanel(oPC); + else NuiDestroy(oPC, NuiFindWindow(oPC, "widgetbuffwin")); + } + if(sElem == "lock_buff_widget_check") + { + int bBuffLockWidget = JsonGetInt(NuiGetBind(oPC, nToken, "lock_buff_widget_check")); + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + if(bBuffLockWidget) jMenuData = JsonArraySet(jMenuData, 3, JsonBool(TRUE)); + jMenuData = JsonArraySet(jMenuData, 4, JsonBool(bBuffLockWidget)); + SetBuffDatabaseJson(oPC, "spells", jMenuData, "menudata"); + NuiSetBind(oPC, nToken, "buff_widget_check", JsonBool(TRUE)); + PopupWidgetBuffGUIPanel(oPC); + } + if(sElem == "chbx_no_monster_check_check") + { + int bNoCheckMonsters = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + SetLocalInt(oPC, FB_NO_MONSTER_CHECK, bNoCheckMonsters); + } + } + } + //************************************************************************** + // Spell Buffing. + else if(sWndId == "widgetbuffwin") + { + if(sEvent == "click") + { + string sList; + if(sElem == "btn_one") sList = "list1"; + if(sElem == "btn_two") sList = "list2"; + if(sElem == "btn_three") sList = "list3"; + if(sElem == "btn_four") sList = "list4"; + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + jMenuData = JsonArraySet(jMenuData, 0, JsonString(sList)); + SetBuffDatabaseJson(oPC, "spells", jMenuData, "menudata"); + CastSavedBuffSpells(oPC); + } + } +} +void SetBuffDatabaseString(object oPlayer, string sDataField, string sData, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "UPDATE BUFF_TABLE SET " + sDataField + " = @data WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@data", sData); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + SqlStep (sql); +} +string GetBuffDatabaseString(object oPlayer, string sDataField, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "SELECT " + sDataField + " FROM BUFF_TABLE WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + if (SqlStep (sql)) return SqlGetString (sql, 0); + else return ""; +} +void SetBuffDatabaseJson (object oPlayer, string sDataField, json jData, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "UPDATE BUFF_TABLE SET " + sDataField + " = @data WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindJson (sql, "@data", jData); + SqlBindString (sql, "@name", sName); + SqlBindString (sql, "@tag", sTag); + SqlStep (sql); +} +json GetBuffDatabaseJson (object oPlayer, string sDataField, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "SELECT " + sDataField + " FROM BUFF_TABLE WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", 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) +{ + 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)); +} +void CastSavedBuffSpells(object oPC) +{ + // Lets make sure the save button is off! + if(GetEventScript(oPC, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT) == "pc_savebuffs") + { + 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); + } + int nMainWindow = NuiFindWindow(oPC, "plbuffwin"); + if(nMainWindow) NuiSetBind(oPC, nMainWindow, "btn_save", JsonBool(FALSE)); + ai_SendMessages("Saving spells to the list has been turned off.", AI_COLOR_YELLOW, oPC); + } + float fDistance; + if(!GetLocalInt(oPC, FB_NO_MONSTER_CHECK)) + { + // Check for monsters! We cannot let them buff if they are close to the enemy! + object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oPC); + fDistance = GetDistanceBetween(oPC, oEnemy); + } + if(fDistance > 30.0f || fDistance == 0.0) + { + string sName; + float fDelay = 0.1f; + int nSpell, nClass, nLevel, nMetamagic, nDomain, nSpellReady, nIndex = 0; + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + string sList = JsonGetString(JsonArrayGet(jMenuData, 0)); + json jSpell, jSpells = GetBuffDatabaseJson(oPC, "spells", sList); + while(nIndex <= BUFF_MAX_SPELLS) + { + jSpell = JsonArrayGet(jSpells, nIndex); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + 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) + { + DelayCommand(fDelay, ai_SendMessages("Cannot quick cast " + sName + " because the " + sTargetName + " is not here!", AI_COLOR_RED, oPC)); + } + else + { + 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(oPC, nSpell, nClass, nLevel, nMetamagic, nDomain); + if(nSpellReady == TRUE) + { + DelayCommand(fDelay, CastBuffSpell(oPC, oTarget, nSpell, nClass, nMetamagic, nDomain, sList, sName)); + } + else if(nSpellReady == -1) + { + DelayCommand(fDelay, ai_SendMessages("Cannot quick cast " + sName + " because it is not ready to cast!", AI_COLOR_RED, oPC)); + } + 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; + nIndex ++; + } + if(nIndex == 0 && !NuiFindWindow(oPC, "plbuffwin")) ExecuteScript("pi_buffing", oPC); + } + else ai_SendMessages("Enemies are too close for you to cast all your buff spells!", AI_COLOR_RED, oPC); +} +int GetSpellReady(object oCaster, int nSpell, int nClass, int nLevel, int nMetamagic, int nDomain) +{ + int nIndex, nMaxIndex, nMSpell, nMmSpell, nDSpell, nSubRadSpell, nSubSpell; + string sSubRadSpell; + if(StringToInt(Get2DAString("classes", "MemorizesSpells", nClass))) + { + int nSpellMemorized; + while(nIndex < nMaxIndex) + { + nMSpell = GetMemorizedSpellId(oCaster, nClass, nLevel, nIndex); + if(nSpell == nMSpell) + { + nMmSpell = GetMemorizedSpellMetaMagic(oCaster, nClass, nLevel, nIndex); + nDSpell = GetMemorizedSpellIsDomainSpell(oCaster, nClass, nLevel, nIndex); + //ai_Debug("pe_buffing", "308", "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; + } + } + } + for(nSubRadSpell = 1; nSubRadSpell < 5; nSubRadSpell++) + { + 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)) + { + if(nDSpell == nDomain) return TRUE; + } + } + } + nIndex ++; + } + if(nSpellMemorized) return -1; + return -2; + } + else + { + int nSpellKnown; + nMaxIndex = GetKnownSpellCount(oCaster, nClass, nLevel); + while(nIndex < nMaxIndex) + { + nMSpell = GetKnownSpellId(oCaster, nClass, nLevel, nIndex); + if(nSpell == nMSpell) + { + nSpellKnown = TRUE; + if(GetSpellUsesLeft(oCaster, nClass, nSpell)) return TRUE; + } + for(nSubRadSpell = 1; nSubRadSpell < 5; nSubRadSpell++) + { + sSubRadSpell = "SubRadSpell" + IntToString(nSubRadSpell); + if(nSpell == StringToInt(Get2DAString("spells", sSubRadSpell, nMSpell))) + { + nSpellKnown = TRUE; + if(GetSpellUsesLeft(oCaster, nClass, nSpell)) return TRUE; + } + } + nIndex ++; + } + if(nSpellKnown) return -3; + return -4; + } + return -2; +} +void PopupWidgetBuffGUIPanel(object oPC) +{ + // 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)); + // 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); + 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 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(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 += 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); + 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"); + // 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)); + 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/_module/nss/pe_crafting.nss b/_module/nss/pe_crafting.nss new file mode 100644 index 00000000..a1a8fac9 --- /dev/null +++ b/_module/nss/pe_crafting.nss @@ -0,0 +1,2155 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pe_crafting +//////////////////////////////////////////////////////////////////////////////// + Used with pi_crafting to run the crafting plugin events for + Philos Single Player Enhancements. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" +#include "nw_inc_gff" +#include "0i_main" +#include "0i_items" +// Maximum model number for all items except weapons. +const int CRAFT_MAX_MODEL_NUMBER = 999; + +struct stWeaponAppearance +{ + object oItem; + int nModel; + int nColor; + string sPart; +}; +// 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 string CRAFT_JSON = "CRAFT_JSON"; +const string CRAFT_ORIGINAL_ITEM = "CRAFT_ORIGINAL_ITEM"; +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_MODEL_SPECIAL = "CRAFT_MODEL_SPECIAL"; +const string CRAFT_ITEM_TYPE = "CRAFT_ITEM_TYPE"; +const string CRAFT_WEAPON_MOD_TOP = "CRAFT_WEAPON_MOD_TOP"; +const string CRAFT_WEAPON_MOD_MID = "CRAFT_WEAPON_MOD_MID"; +const string CRAFT_WEAPON_MOD_BOT = "CRAFT_WEAPON_MOD_BOT"; +const string CRAFT_WEAPON_COL_TOP = "CRAFT_WEAPON_COL_TOP"; +const string CRAFT_WEAPON_COL_MID = "CRAFT_WEAPON_COL_MID"; +const string CRAFT_WEAPON_COL_BOT = "CRAFT_WEAPON_COL_BOT"; +const string CRAFT_COPY_ITEM = "CRAFT_COPY_ITEM"; +const string CRAFT_COPY_ITEM_TYPE = "CRAFT_COPY_ITEM_TYPE"; +const string CRAFT_COPY_MODEL = "CRAFT_COPY_MODEL"; +const string CRAFT_COPY_COLOR = "CRAFT_COPY_COLOR"; +const string CRAFT_COPY_PART_COLOR = "CRAFT_COPY_PART_COLOR"; +const string CRAFT_ARMOR_AC = "CRAFT_ARMOR_AC"; +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"; +// The tags for containers used to do some crafting. +const string CRAFT_TEMPLATE = "x3_plc_basket"; +const string CRAFT_CONTAINER = "CRAFT_CONTAINER"; +// Used in the crafting GUI to copy an item to be pasted to another item later. +void CopyCraftingItem(object oPC, object oItem); +// Used in the crafting GUI to paste a copy of an item to another item. +object PasteCraftingItem(object oPC, object oTarget, object oItem); +int GetItemSelectedEquipSlot(int nItemSelected); +int GetArmorModelSelected(object oPC); +object ChangeItemsAppearance(object oPC, object oTarget, int nToken, object oItem, int nDirection, string sPart); +// Checks to see if the item can be crafted. +// bPasteCheck is a special check when an item is being pasted. +int CanCraftItem(object oPC, object oItem, int nToken, int bPasteCheck = FALSE); +object RandomizeItemsCraftAppearance(object oPlayer, object oTarget, int nToken, object oItem); +// Returns the correct item based on the crafting menu selected item. +object GetSelectedItem(object oTarget, int nItemSelected); +// Cancels the crafted item for the player and restoring the original. +void CancelCraftedItem(object oPlayer, object oTarget); +// Gets the colorId from a image of the color pallet. +// Thanks Zunath for the base code. +int GetColorPalletId(object oPC, int nToken); +// Sets the pointer based on current Item, Part, and Material selected. +void SetColorPalletPointer(object oPC, int nToken, object oItem); +// Locks/Unlocks specific buttons when an item has been changed. +void LockItemInCraftingWindow(object oPC, object oItem, object oTarget, int nToken); +// Locks/Unlocks specific buttons when an item has been cleared. +void ClearItemInCraftingWindow(object oPC, object oItem, int nToken); +// Saves the crafted item for the player removing the original. +void SaveCraftedItem(object oPC, object oTarget, int nToken); +// Remove Effect of type specified from oCreature; +// sEffectTag is the tag of the effect to remove. +// Feat, Class, Racial. +void RemoveTagedEffects(object oCreature, string sEffectTag); +// Returns TRUE/FALSE if item has temporary item property. +int CheckForTemporaryItemProperty(object oItem); +// Updates the model number text in the NUI menu. +void SetModelNumberText(object oPC, object oTarget, int nToken); +// 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); +// Creates the item editing menu. +void CreateItemGUIPanel(object oPC, object oTarget); +// Events for ItemGUIPanel +void CraftItemInfoEvents(object oPC, int nToken); +// Creates the save/load menu for items. +//void CreateDresserGUIPanel(object oPC, object oTarget); + +int GetColorIDChange(object oItem, int nType, int nIndex, int nChange) +{ + int nColorId = GetItemAppearance(oItem, nType, nIndex) + nChange; + if(nColorId > 175) return 0; + if(nColorId < 0) return 175; + return nColorId; +} +void main() +{ + // Get the last player to use targeting mode + object oPC = GetLastPlayerToSelectTarget(); + string sTargetMode = GetLocalString(oPC, AI_TARGET_MODE); + if(oPC == OBJECT_SELF && sTargetMode != "") + { + // Get the targeting mode data + object oTarget = GetTargetingModeSelectedObject(); + //vector vTarget = GetTargetingModeSelectedPosition(); + //location lLocation = Location(GetArea(oPC), vTarget, GetFacing(oPC)); + //object oObject = GetLocalObject(oPC, "AI_TARGET_OBJECT"); + // If the user manually exited targeting mode without selecting a target, return + if(!GetIsObjectValid(oTarget))// && vTarget == Vector()) + { + return; + } + // Targeting code here. + if(sTargetMode == "SELECT_TARGET") + { + if(GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN || + ai_GetIsCharacter(oTarget)) + { + SetLocalObject(oPC, CRAFT_TARGET, oTarget); + AttachCamera(oPC, oTarget); + ExecuteScript("pi_crafting", oPC); + } + 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); + } + else + { + object oPC = NuiGetEventPlayer(); + int nToken = NuiGetEventWindow(); + string sWndId = NuiGetWindowId (oPC, nToken); + if(sWndId == "craft_item_nui") + { + CraftItemInfoEvents(oPC, nToken); + return; + } + string sEvent = NuiGetEventType(); + // We don't use and it causes error windows to go off! Return early! + if(sEvent == "mouseup") return; + string sElem = NuiGetEventElement(); + int nIndex = NuiGetEventArrayIndex(); + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + //SendMessageToPC(oPC, "0e_crafting, 144, sElem: " + sElem + " sEvent: " + sEvent); + //************************************************************************** + // Watch to see if the window moves and save. + if(sElem == "window_geometry" && sEvent == "watch") + { + if(!GetLocalInt (oPC, AI_NO_NUI_SAVE)) + { + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + if(JsonGetType(jCraft) == JSON_TYPE_NULL) jCraft = JsonObject(); + // Get the height, width, x, and y of the window. + json jGeometry = NuiGetBind(oPC, nToken, "window_geometry"); + jCraft = JsonObjectSet(jCraft, "CRAFT_MENU", jGeometry); + SetLocalJson(oPC, CRAFT_JSON, jCraft); + } + return; + } + //************************************************************************** + object oTarget = GetLocalObject(oPC, CRAFT_TARGET); + if(oTarget == OBJECT_INVALID) oTarget = oPC; + // Get the item we are crafting. + int nItemSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + object oItem = GetSelectedItem(oTarget, nItemSelected); + object oOriginalItem = GetLocalObject(oPC, CRAFT_ORIGINAL_ITEM); + if(oItem == OBJECT_INVALID) + { + if(sElem != "btn_cancel") + { + ai_SendMessages("The item we are adjusting is not equiped!", AI_COLOR_RED, oPC); + return; + } + } + else if(oOriginalItem != OBJECT_INVALID && GetTag(oItem) != GetTag(oOriginalItem)) + { + ai_SendMessages(GetName(oItem) + " is not the item you have been adjusting!", AI_COLOR_RED, oPC); + return; + } + // Changing the name needs to be before the cooldown. + if(sElem == "txt_item_name" && sEvent == "watch") + { + string sName = JsonGetString(NuiGetBind(oPC, nToken, "txt_item_name")); + SetName(oItem, sName); + int nToken2 = NuiFindWindow(oPC, "craft_item_nui"); + if(nToken2) NuiSetBind(oPC, nToken2, "txt_item_name", JsonString(sName)); + 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)); + // They have selected a color. + if(sElem == "color_pallet") + { + int nColorId, nChange; + object oNewItem; + if(sEvent == "mousedown") + { + // Get the color they selected from the color pallet cell. + nColorId = GetColorPalletId(oPC, nToken); + } + else if(sEvent == "mousescroll") + { + float nMouseScroll = JsonGetFloat(JsonObjectGet(JsonObjectGet(NuiGetEventPayload(), "mouse_scroll"), "y")); + nChange = FloatToInt(nMouseScroll); + } + else return; + if(!CanCraftItem(oPC, oItem, nToken)) return; + int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + int nBaseItemType = GetBaseItemType(oItem); + int nAllColor = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ALL_COLOR)); + if(!nAllColor && nBaseItemType == BASE_ITEM_ARMOR) + { + int nIndex; + int nModelSelected = GetArmorModelSelected(oPC); + int nLeftColor = JsonGetInt(JsonObjectGet(jCraft, CRAFT_LEFT_PART_COLOR)); + int nRightColor = JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR)); + 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) + { + nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + if(nChange) nColorId = GetColorIDChange(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nChange); + oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nColorId, TRUE); + DestroyObject(oItem); + } + else + { + if(nRightColor) + { + // Color Right side. + nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + if(nChange) nColorId = GetColorIDChange(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nChange); + oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nColorId, TRUE); + DestroyObject(oItem); + // Fix buttons. + NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(TRUE)); + if(nLeftColor) + { + // If we are doing the left side then add one to get the left side. + // Note: Right Thigh and Left Thigh are backwards so this fixes that! + if (nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected = nModelSelected - 1; + else nModelSelected = nModelSelected + 1; + nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + if(nChange) nColorId = GetColorIDChange(oNewItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nChange); + oItem = CopyItemAndModify(oNewItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nColorId, TRUE); + DestroyObject(oNewItem); + oNewItem = oItem; + // Fix buttons. + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(TRUE)); + } + } + else if(nLeftColor) + { + // If we are doing the left side then add one to get the left side. + // Note: Right Thigh and Left Thigh are backwards so this fixes that! + if (nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected = nModelSelected - 1; + else nModelSelected = nModelSelected + 1; + nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + if(nChange) nColorId = GetColorIDChange(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nChange); + oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, nColorId, TRUE); + DestroyObject(oItem); + // Fix buttons. + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(TRUE)); + } + } + } + else + { + 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); + // Equip new item. + if(nBaseItemType == BASE_ITEM_CLOAK) AssignCommand (oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_CLOAK)); + else if(nBaseItemType == BASE_ITEM_HELMET) AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_HEAD)); + else if(nBaseItemType == BASE_ITEM_ARMOR) AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_CHEST)); + } + else if(sEvent == "watch") + { + // The player is changing the item they are crafting. + if(sElem == "item_combo_selected") + { + int nSelected = JsonGetInt(NuiGetBind (oPC, nToken, sElem)); + oItem = GetSelectedItem(oTarget, nSelected); + if(oItem == OBJECT_INVALID) + { + ai_SendMessages("There is not an item to modify!", AI_COLOR_RED, oPC); + int nItem = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + NuiSetBind(oPC, nToken, "item_combo_selected", JsonInt(nItem)); + return; + } + jCraft = JsonObjectSet(jCraft, CRAFT_ITEM_SELECTION, JsonInt(nSelected)); + // Set button for cloak and helms. + if(nSelected == 1 || nSelected == 2) + { + int nHidden = GetHiddenWhenEquipped(oItem); + if(nHidden) jCraft = JsonObjectSet(jCraft, CRAFT_MODEL_SELECTION, JsonInt(1)); + else jCraft = JsonObjectSet(jCraft, CRAFT_MODEL_SELECTION, JsonInt(0)); + } + else jCraft = JsonObjectSet(jCraft, CRAFT_MODEL_SELECTION, JsonInt(0)); + SetLocalJson(oPC, CRAFT_JSON, jCraft); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_crafting", oPC); + } + // They have selected a part to change. + else if(sElem == "model_combo_selected") + { + int nSelected = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + jCraft = JsonObjectSet(jCraft, CRAFT_MODEL_SELECTION, JsonInt(nSelected)); + SetLocalJson(oPC, CRAFT_JSON, jCraft); + SetModelNumberText(oPC, oTarget, nToken); + int nItem = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + if(nItem == 1) // Cloak + { + if(!CanCraftItem(oPC, oItem, nToken)) return; + object oItem = GetItemInSlot(INVENTORY_SLOT_CLOAK, oTarget); + if(nSelected == 1) SetHiddenWhenEquipped(oItem, TRUE); + else SetHiddenWhenEquipped(oItem, FALSE); + LockItemInCraftingWindow(oPC, oItem, oTarget, nToken); + } + else if(nItem == 2) // Headgear + { + if(!CanCraftItem(oPC, oItem, nToken)) return; + object oItem = GetItemInSlot(INVENTORY_SLOT_HEAD, oTarget); + if(nSelected == 1) SetHiddenWhenEquipped(oItem, TRUE); + else SetHiddenWhenEquipped(oItem, FALSE); + LockItemInCraftingWindow(oPC, oItem, oTarget, nToken); + } + else if(nItem == 4 && ai_GetIsShield(oItem)) + { + if(!CanCraftItem(oPC, oItem, nToken)) return; + object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget); + if(nSelected == 1) SetHiddenWhenEquipped(oItem, TRUE); + else SetHiddenWhenEquipped(oItem, FALSE); + LockItemInCraftingWindow(oPC, oItem, oTarget, nToken); + } + if(ai_GetIsWeapon(oItem)) + { + // Clearing sets the module to 0 triggering an extra call. + if(GetLocalInt(oPC, AI_NO_NUI_SAVE)) return; + if(!CanCraftItem(oPC, oItem, nToken)) return; + int nVisual; + itemproperty ipProperty = GetFirstItemProperty(oItem); + while(GetIsItemPropertyValid(ipProperty)) + { + if(GetItemPropertyType(ipProperty) == ITEM_PROPERTY_VISUALEFFECT) + { + RemoveItemProperty(oItem, ipProperty); + } + ipProperty = GetNextItemProperty(oItem); + } + if(nSelected == 1) nVisual = ITEM_VISUAL_ACID; + else if(nSelected == 2) nVisual = ITEM_VISUAL_COLD; + else if(nSelected == 3) nVisual = ITEM_VISUAL_ELECTRICAL; + else if(nSelected == 4) nVisual = ITEM_VISUAL_EVIL; + else if(nSelected == 5) nVisual = ITEM_VISUAL_FIRE; + else if(nSelected == 6) nVisual = ITEM_VISUAL_HOLY; + else if(nSelected == 7) nVisual = ITEM_VISUAL_SONIC; + if(nVisual) + { + ipProperty = ItemPropertyVisualEffect(nVisual); + AddItemProperty(DURATION_TYPE_PERMANENT, ipProperty, oItem); + } + LockItemInCraftingWindow(oPC, oItem, oTarget, nToken); + } + } + } + else if(sEvent == "click") + { + if(sElem == "btn_info") + { + SetLocalObject(oPC, "CRAFT_INFO_ITEM", oItem); + CreateItemGUIPanel(oPC, oItem); + } + //else if(sElem == "btn_wardrobe") CreateDresserGUIPanel(oPC, oTarget); + // Random button to change items looks randomly. + else if(sElem == "btn_randomize") + { + if(CanCraftItem(oPC, oItem, nToken)) + { + oItem = RandomizeItemsCraftAppearance(oPC, oTarget, nToken, oItem); + LockItemInCraftingWindow(oPC, oItem, oTarget, nToken); + } + } + // Save any changes made to the selected item. + else if(sElem == "btn_save") + { + SaveCraftedItem(oPC, oTarget, nToken); + } + // Selecte target to change clothing on. + else 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 either your charcter or a henchman to craft their equipment.", AI_COLOR_YELLOW, oPC); + DeleteLocalObject(oPC, CRAFT_ORIGINAL_ITEM); + DeleteLocalObject(oPC, CRAFT_TARGET); + DeleteLocalObject(oPC, "CRAFT_INFO_ITEM"); + if(GetLocalInt(oPC, CRAFT_ULTRALIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_ULTRALIGHT); + DeleteLocalInt(oPC, CRAFT_ULTRALIGHT); + } + if(GetLocalInt(oPC, CRAFT_HIGHLIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_HIGHLIGHT); + DeleteLocalInt(oPC, CRAFT_HIGHLIGHT); + } + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE , MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + // Cancel any changes made to the selected item. + else if(sElem == "btn_cancel") + { + // If the button is on cancel then clear the item. + if(JsonGetString(NuiGetBind(oPC, nToken, "btn_cancel_label")) == "Cancel") + { + CancelCraftedItem(oPC, oTarget); + ClearItemInCraftingWindow(oPC, oItem, nToken); + DelayCommand(0.5, NuiDestroy(oPC, nToken)); + DelayCommand(0.5, ExecuteScript("pi_crafting", oPC)); + } + // If the button is on Exit not Cancel then exit. + else + { + AssignCommand(oPC, RestoreCameraFacing()); + AttachCamera(oPC, oPC); + DeleteLocalObject(oPC, CRAFT_ORIGINAL_ITEM); + DeleteLocalObject(oPC, CRAFT_TARGET); + DeleteLocalObject(oPC, "CRAFT_INFO_ITEM"); + NuiDestroy(oPC, nToken); + nToken = NuiFindWindow(oPC, "craft_item_nui"); + if(nToken) NuiDestroy(oPC, nToken); + if(GetLocalInt(oPC, CRAFT_ULTRALIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_ULTRALIGHT); + DeleteLocalInt(oPC, CRAFT_ULTRALIGHT); + } + if(GetLocalInt(oPC, CRAFT_HIGHLIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_HIGHLIGHT); + DeleteLocalInt(oPC, CRAFT_HIGHLIGHT); + } + } + } + // Get the previous model of the selected item. + else if(GetStringLeft(sElem, 9) == "btn_prev_") + { + if(CanCraftItem(oPC, oItem, nToken)) + { + oItem = ChangeItemsAppearance(oPC, oTarget, nToken, oItem, -1, GetStringRight(sElem, 1)); + LockItemInCraftingWindow(oPC, oItem, oTarget, nToken); + } + } + // Get the next model of the selected item. + else if(GetStringLeft(sElem, 9) == "btn_next_") + { + if(CanCraftItem(oPC, oItem, nToken)) + { + oItem = ChangeItemsAppearance(oPC, oTarget, nToken, oItem, 1, GetStringRight(sElem, 1)); + LockItemInCraftingWindow(oPC, oItem, oTarget, nToken); + } + } + else if(sElem == "btn_highlight") + { + if(GetLocalInt(oPC, CRAFT_HIGHLIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_HIGHLIGHT); + DeleteLocalInt(oPC, CRAFT_HIGHLIGHT); + NuiSetBind(oPC, nToken, "btn_highlight", JsonBool(FALSE)); + } + else + { + if(GetLocalInt(oPC, CRAFT_ULTRALIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_ULTRALIGHT); + DeleteLocalInt(oPC, CRAFT_ULTRALIGHT); + } + SetLocalInt(oPC, CRAFT_HIGHLIGHT, TRUE); + effect eLight = EffectVisualEffect(VFX_DUR_LIGHT_WHITE_20); + eLight = TagEffect(eLight, CRAFT_HIGHLIGHT); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLight, oTarget); + NuiSetBind(oPC, nToken, "btn_highlight", JsonBool(TRUE)); + } + } + else if(sElem == "btn_left_part_color") + { + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonInt(FALSE)); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_LEFT_PART_COLOR, JsonInt(TRUE)); + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(TRUE)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonInt(FALSE)); + NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(FALSE)); + SetColorPalletPointer(oPC, nToken, oItem); + } + else if(sElem == "btn_all_color") + { + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonInt(TRUE)); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(TRUE)); + jCraft = JsonObjectSet(jCraft, CRAFT_LEFT_PART_COLOR, JsonInt(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonInt(FALSE)); + NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(FALSE)); + SetColorPalletPointer(oPC, nToken, oItem); + } + else if(sElem == "btn_right_part_color") + { + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonInt(FALSE)); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_LEFT_PART_COLOR, JsonInt(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonInt(TRUE)); + NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(TRUE)); + SetColorPalletPointer(oPC, nToken, oItem); + } + else if(sElem == "btn_right_part_reset") + { + if(CanCraftItem(oPC, oItem, nToken)) + { + int nIndex; + int nModelSelected = GetArmorModelSelected(oPC); + int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + object oNewItem; + 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) + { + nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, 255, TRUE); + DestroyObject(oItem); + } + else + { + nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, 255, TRUE); + DestroyObject(oItem); + } + // Lock the new item so they can't change it on the character. + LockItemInCraftingWindow(oPC, oNewItem, oTarget, nToken); + // Equip new item. + AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_CHEST)); + // Fix buttons. + NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonInt(FALSE)); + int nLeft = JsonGetInt(NuiGetBind(oPC, nToken, "btn_left_part_color")); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(!nLeft)); + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonInt(!nLeft)); + NuiSetBind(oPC, nToken, "btn_right_part_reset_event", JsonBool(FALSE)); + nLeft = JsonGetInt(NuiGetBind(oPC, nToken, "btn_left_part_reset_event")); + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(nLeft)); + SetColorPalletPointer(oPC, nToken, oNewItem); + } + } + else if(sElem == "btn_all_reset") + { + if(CanCraftItem(oPC, oItem, nToken)) + { + int nIndex, nColor; + json jItem = ObjectToJson(oItem, TRUE); + string sColor, sPartName; + for(nIndex = 0;nIndex < 19;nIndex++) + { + sPartName = "APart_" + IntToString(nIndex) + "_Col_"; + for(nColor = 0;nColor < 6;nColor++) + { + sColor = IntToString(nColor); + if(JsonGetType(GffGetByte(jItem, sPartName + sColor)) != JSON_TYPE_NULL) + { + jItem = GffRemoveByte(jItem, sPartName + sColor); + } + } + } + object oNewItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_CHEST)); + DestroyObject(oItem); + // Lock the new item so they can't change it on the character. + LockItemInCraftingWindow(oPC, oNewItem, oTarget, nToken); + // Fix buttons. + NuiSetBind(oPC, nToken, "btn_right_part_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonInt(FALSE)); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(TRUE)); + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonInt(TRUE)); + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_RIGHT_PART_COLOR, JsonInt(FALSE)); + 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)); + SetColorPalletPointer(oPC, nToken, oNewItem); + } + } + else if(sElem == "btn_left_part_reset") + { + if(CanCraftItem(oPC, oItem, nToken)) + { + int nModelSelected = GetArmorModelSelected(oPC); + if (nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected = nModelSelected - 1; + else nModelSelected = nModelSelected + 1; + int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + int nIndex = ITEM_APPR_ARMOR_NUM_COLORS + (nModelSelected * ITEM_APPR_ARMOR_NUM_COLORS) + nMaterialSelected; + object oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_ARMOR_COLOR, nIndex, 255, TRUE); + DestroyObject(oItem); + // Lock the new item so they can't change it on the character. + LockItemInCraftingWindow(oPC, oNewItem, oTarget, nToken); + // Equip new item. + AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_CHEST)); + // Fix buttons. + NuiSetBind(oPC, nToken, "btn_left_part_color", JsonBool(FALSE)); + jCraft = JsonObjectSet(jCraft, CRAFT_LEFT_PART_COLOR, JsonInt(FALSE)); + int nRight = JsonGetInt(NuiGetBind(oPC, nToken, "btn_right_part_color")); + NuiSetBind(oPC, nToken, "btn_all_color", JsonBool(!nRight)); + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonInt(!nRight)); + NuiSetBind(oPC, nToken, "btn_left_part_reset_event", JsonBool(FALSE)); + nRight = JsonGetInt(NuiGetBind(oPC, nToken, "btn_right_part_reset_event")); + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(nRight)); + SetColorPalletPointer(oPC, nToken, oNewItem); + } + } + // They have changed the material (color item) for the item. + else if(GetStringLeft(sElem, 13) == "btn_material_") + { + 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) + { + sColorPallet = "gui_pal_tattoo"; + NuiSetBind(oPC, nToken, "armor_block_1", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "armor_block_2", JsonBool(FALSE)); + } + else + { + sColorPallet = "armor_pallet"; + if(ResManGetAliasFor(sColorPallet, RESTYPE_TGA) == "") + { + sColorPallet = "gui_pal_tattoo"; + NuiSetBind(oPC, nToken, "armor_block_1", JsonBool(TRUE)); + } + } + NuiSetBind(oPC, nToken, "color_pallet_image", JsonString (sColorPallet)); + SetLocalString(oPC, CRAFT_COLOR_PALLET, sColorPallet); + SetColorPalletPointer(oPC, nToken, oItem); + } + SetLocalJson(oPC, CRAFT_JSON, jCraft); + } + else if(sEvent == "mousedown") + { + int nMouseButton = JsonGetInt(JsonObjectGet(NuiGetEventPayload(), "mouse_btn")); + if(nMouseButton == NUI_MOUSE_BUTTON_RIGHT) + { + AssignCommand(oPC, PlaySound("gui_button")); + if(sElem == "btn_highlight") + { + if(GetLocalInt(oPC, CRAFT_ULTRALIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_ULTRALIGHT); + DeleteLocalInt(oPC, CRAFT_ULTRALIGHT); + NuiSetBind(oPC, nToken, "btn_highlight", JsonBool(FALSE)); + } + else + { + if(GetLocalInt(oPC, CRAFT_HIGHLIGHT)) + { + RemoveTagedEffects(oTarget, CRAFT_HIGHLIGHT); + DeleteLocalInt(oPC, CRAFT_HIGHLIGHT); + } + SetLocalInt(oPC, CRAFT_ULTRALIGHT, TRUE); + effect eLight = EffectVisualEffect(VFX_DUR_ULTRAVISION); + eLight = TagEffect(eLight, CRAFT_ULTRALIGHT); + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLight, oTarget); + NuiSetBind(oPC, nToken, "btn_highlight", JsonBool(TRUE)); + } + } + } + } + } +} +/*void CopyCraftingItem(object oPC, object oItem) +{ + //ai_Debug("pe_crafting", "295", JsonDump(ObjectToJson(oItem), 2)); + json jItem = ObjectToJson(oItem); + + SetLocalInt(oPC, CRAFT_COPY_ITEM, TRUE); + int nSelected = GetLocalInt(oPC, CRAFT_ITEM_SELECTION); + if (ai_GetIsWeapon(oItem)) + { + // Copy the base item type; + SetLocalInt(oPC, CRAFT_COPY_ITEM_TYPE, GetBaseItemType(oItem)); + // Copy each model/color & save to variables. + int nIndex = 1; + string sIndex; + while(nIndex <= 3) + { + sIndex = IntToString(nIndex); + SetLocalInt(oPC, CRAFT_COPY_MODEL + sIndex, JsonGetInt(GffGetByte(jItem, "ModelPart" + sIndex))); + nIndex++; + } + } + else if (nSelected == 0) + { + // Copy the armors AC so we can check it. + SetLocalInt(oPC, CRAFT_ARMOR_AC, ai_GetArmorBonus(oItem)); + // Copy an per part colors if they exist. + int nPart, nColor, nPartColor; + string sPart, sColor; + while(nPart <= 18) + { + sPart = IntToString(nPart); + nColor = 0; + while(nColor <= 5) + { + sColor = IntToString(nColor); + if(GffGetFieldExists(jItem, "APart_" + sPart + "_Col_" + sColor, GFF_FIELD_TYPE_BYTE)) + { + // Shift the number up by 1 so we can save as a variable and not use 0! + nPartColor = JsonGetInt(GffGetByte(jItem, "APart_" + sPart + "_Col_" + sColor)) + 1; + SetLocalInt(oPC, CRAFT_COPY_PART_COLOR + sPart + sColor, nPartColor); + } + nColor++; + } + nPart++; + } + // Copy each model & save to variables. + SetLocalInt(oPC, "CRAFT_COPY_MODEL0", JsonGetInt(GffGetByte(jItem, "ArmorPart_Belt"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL1", JsonGetInt(GffGetByte(jItem, "ArmorPart_LBicep"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL2", JsonGetInt(GffGetByte(jItem, "ArmorPart_LFArm"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL3", JsonGetInt(GffGetByte(jItem, "ArmorPart_LFoot"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL4", JsonGetInt(GffGetByte(jItem, "ArmorPart_LHand"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL5", JsonGetInt(GffGetByte(jItem, "ArmorPart_LShin"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL6", JsonGetInt(GffGetByte(jItem, "ArmorPart_LShoul"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL7", JsonGetInt(GffGetByte(jItem, "ArmorPart_LThigh"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL8", JsonGetInt(GffGetByte(jItem, "ArmorPart_Neck"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL9", JsonGetInt(GffGetByte(jItem, "ArmorPart_Pelvis"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL10", JsonGetInt(GffGetByte(jItem, "ArmorPart_RBicep"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL11", JsonGetInt(GffGetByte(jItem, "ArmorPart_RFArm"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL12", JsonGetInt(GffGetByte(jItem, "ArmorPart_RFoot"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL13", JsonGetInt(GffGetByte(jItem, "ArmorPart_RHand"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL14", JsonGetInt(GffGetByte(jItem, "ArmorPart_RShin"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL15", JsonGetInt(GffGetByte(jItem, "ArmorPart_RShoul"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL16", JsonGetInt(GffGetByte(jItem, "ArmorPart_RThigh"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL17", JsonGetInt(GffGetByte(jItem, "ArmorPart_Robe"))); + SetLocalInt(oPC, "CRAFT_COPY_MODEL18", JsonGetInt(GffGetByte(jItem, "ArmorPart_Torso"))); + // Copy each color and save to variables. + SetLocalInt(oPC, "CRAFT_COPY_COLOR0", JsonGetInt(GffGetByte(jItem, "Cloth1Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR1", JsonGetInt(GffGetByte(jItem, "Cloth2Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR2", JsonGetInt(GffGetByte(jItem, "Leather1Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR3", JsonGetInt(GffGetByte(jItem, "Leather2Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR4", JsonGetInt(GffGetByte(jItem, "Metal1Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR5", JsonGetInt(GffGetByte(jItem, "Metal2Color"))); + } +else + { + // Copy the base item type; + SetLocalInt(oPC, CRAFT_COPY_ITEM_TYPE, GetBaseItemType(oItem)); + // Copy the base item type; + SetLocalInt(oPC, "CRAFT_COPY_MODEL0", JsonGetInt(GffGetByte(jItem, "ModelPart1"))); + // Copy each color and save to variables. + SetLocalInt(oPC, "CRAFT_COPY_COLOR0", JsonGetInt(GffGetByte(jItem, "Cloth1Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR1", JsonGetInt(GffGetByte(jItem, "Cloth2Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR2", JsonGetInt(GffGetByte(jItem, "Leather1Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR3", JsonGetInt(GffGetByte(jItem, "Leather2Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR4", JsonGetInt(GffGetByte(jItem, "Metal1Color"))); + SetLocalInt(oPC, "CRAFT_COPY_COLOR5", JsonGetInt(GffGetByte(jItem, "Metal2Color"))); + } + // Send message that it has been copied. + ai_SendMessages(GetName (oItem) + " appearance has been copied!", AI_COLOR_GREEN, oPC); +} + +// Used in the crafting GUI to paste a copy of an item to another item. +object PasteCraftingItem (object oPC, object oTarget, object oItem) +{ + int nModelPartNum; + object oChestItem; + int nSelected = GetLocalInt(oPC, CRAFT_ITEM_SELECTION); + object oBuildContainer = GetObjectByTag(CRAFT_CONTAINER); + // Move the item to the building container. + oChestItem = CopyItem(oItem, oBuildContainer, TRUE); + DestroyObject(oItem); + json jItem = ObjectToJson(oChestItem, TRUE); + if (ai_GetIsWeapon(oChestItem)) + { + // Copy each model & save to variables. + int nIndex = 1; + string sIndex; + while(nIndex <= 3) + { + sIndex = IntToString(nIndex); + jItem = GffReplaceByte(jItem,"ModelPart" + sIndex, GetLocalInt(oPC, CRAFT_COPY_MODEL + sIndex)); + jItem = GffReplaceWord(jItem,"xModelPart" + sIndex, GetLocalInt(oPC, CRAFT_COPY_MODEL + sIndex)); + DeleteLocalInt(oPC, CRAFT_COPY_MODEL + sIndex); + nIndex++; + } + oItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + // Equip new item. + AssignCommand(oTarget, ActionEquipItem (oItem, INVENTORY_SLOT_RIGHTHAND)); + } + // Armor. + else if (nSelected == 0) + { + // Paste per part colors if they exist. + int nPart, nColor, nPartColor; + string sPart, sColor; + while(nPart <= 18) + { + sPart = IntToString(nPart); + nColor = 0; + while(nColor <= 5) + { + sColor = IntToString(nColor); + nPartColor = GetLocalInt(oPC, CRAFT_COPY_PART_COLOR + sPart + sColor); + if(nPartColor > 0) + { + // Shift the number down by 1 since we can not use 0 in the variable! + nPartColor = nPartColor - 1; + if(GffGetFieldExists(jItem, "APart_" + sPart + "_Col_" + sColor, GFF_FIELD_TYPE_BYTE)) + { + jItem = GffReplaceByte(jItem, "APart_" + sPart + "_Col_" + sColor, nPartColor); + } + else jItem = GffAddByte(jItem, "APart_" + sPart + "_Col_" + sColor, nPartColor); + DeleteLocalInt(oPC, "CRAFT_COPY_PART_COLOR" + sPart + sColor); + } + nColor++; + } + nPart++; + } + jItem = GffReplaceByte(jItem,"ArmorPart_Belt", GetLocalInt(oPC, "CRAFT_COPY_MODEL0")); + jItem = GffReplaceByte(jItem,"ArmorPart_LBicep", GetLocalInt(oPC, "CRAFT_COPY_MODEL1")); + jItem = GffReplaceByte(jItem,"ArmorPart_LFArm", GetLocalInt(oPC, "CRAFT_COPY_MODEL2")); + jItem = GffReplaceByte(jItem,"ArmorPart_LFoot", GetLocalInt(oPC, "CRAFT_COPY_MODEL3")); + jItem = GffReplaceByte(jItem,"ArmorPart_LHand", GetLocalInt(oPC, "CRAFT_COPY_MODEL4")); + jItem = GffReplaceByte(jItem,"ArmorPart_LShin", GetLocalInt(oPC, "CRAFT_COPY_MODEL5")); + jItem = GffReplaceByte(jItem,"ArmorPart_LShoul", GetLocalInt(oPC, "CRAFT_COPY_MODEL6")); + jItem = GffReplaceByte(jItem,"ArmorPart_LThigh", GetLocalInt(oPC, "CRAFT_COPY_MODEL7")); + jItem = GffReplaceByte(jItem,"ArmorPart_Neck", GetLocalInt(oPC, "CRAFT_COPY_MODEL8")); + jItem = GffReplaceByte(jItem,"ArmorPart_Pelvis", GetLocalInt(oPC, "CRAFT_COPY_MODEL9")); + jItem = GffReplaceByte(jItem,"ArmorPart_RBicep", GetLocalInt(oPC, "CRAFT_COPY_MODEL10")); + jItem = GffReplaceByte(jItem,"ArmorPart_RFArm", GetLocalInt(oPC, "CRAFT_COPY_MODEL11")); + jItem = GffReplaceByte(jItem,"ArmorPart_RFoot", GetLocalInt(oPC, "CRAFT_COPY_MODEL12")); + jItem = GffReplaceByte(jItem,"ArmorPart_RHand", GetLocalInt(oPC, "CRAFT_COPY_MODEL13")); + jItem = GffReplaceByte(jItem,"ArmorPart_RShin", GetLocalInt(oPC, "CRAFT_COPY_MODEL14")); + jItem = GffReplaceByte(jItem,"ArmorPart_RShoul", GetLocalInt(oPC, "CRAFT_COPY_MODEL15")); + jItem = GffReplaceByte(jItem,"ArmorPart_RThigh", GetLocalInt(oPC, "CRAFT_COPY_MODEL16")); + jItem = GffReplaceByte(jItem,"ArmorPart_Robe", GetLocalInt(oPC, "CRAFT_COPY_MODEL17")); + jItem = GffReplaceByte(jItem,"ArmorPart_Torso", GetLocalInt(oPC, "CRAFT_COPY_MODEL18")); + jItem = GffReplaceWord(jItem,"xArmorPart_Belt", GetLocalInt(oPC, "CRAFT_COPY_MODEL0")); + jItem = GffReplaceWord(jItem,"xArmorPart_LBice", GetLocalInt(oPC, "CRAFT_COPY_MODEL1")); + jItem = GffReplaceWord(jItem,"xArmorPart_LFArm", GetLocalInt(oPC, "CRAFT_COPY_MODEL2")); + jItem = GffReplaceWord(jItem,"xArmorPart_LFoot", GetLocalInt(oPC, "CRAFT_COPY_MODEL3")); + jItem = GffReplaceWord(jItem,"xArmorPart_LHand", GetLocalInt(oPC, "CRAFT_COPY_MODEL4")); + jItem = GffReplaceWord(jItem,"xArmorPart_LShin", GetLocalInt(oPC, "CRAFT_COPY_MODEL5")); + jItem = GffReplaceWord(jItem,"xArmorPart_LShou", GetLocalInt(oPC, "CRAFT_COPY_MODEL6")); + jItem = GffReplaceWord(jItem,"xArmorPart_LThig", GetLocalInt(oPC, "CRAFT_COPY_MODEL7")); + jItem = GffReplaceWord(jItem,"xArmorPart_Neck", GetLocalInt(oPC, "CRAFT_COPY_MODEL8")); + jItem = GffReplaceWord(jItem,"xArmorPart_Pelvi", GetLocalInt(oPC, "CRAFT_COPY_MODEL9")); + jItem = GffReplaceWord(jItem,"xArmorPart_RBice", GetLocalInt(oPC, "CRAFT_COPY_MODEL10")); + jItem = GffReplaceWord(jItem,"xArmorPart_RFArm", GetLocalInt(oPC, "CRAFT_COPY_MODEL11")); + jItem = GffReplaceWord(jItem,"xArmorPart_RFoot", GetLocalInt(oPC, "CRAFT_COPY_MODEL12")); + jItem = GffReplaceWord(jItem,"xArmorPart_RHand", GetLocalInt(oPC, "CRAFT_COPY_MODEL13")); + jItem = GffReplaceWord(jItem,"xArmorPart_RShin", GetLocalInt(oPC, "CRAFT_COPY_MODEL14")); + jItem = GffReplaceWord(jItem,"xArmorPart_RShou", GetLocalInt(oPC, "CRAFT_COPY_MODEL15")); + jItem = GffReplaceWord(jItem,"xArmorPart_RThig", GetLocalInt(oPC, "CRAFT_COPY_MODEL16")); + jItem = GffReplaceWord(jItem,"xArmorPart_Robe", GetLocalInt(oPC, "CRAFT_COPY_MODEL17")); + jItem = GffReplaceWord(jItem,"xArmorPart_Torso", GetLocalInt(oPC, "CRAFT_COPY_MODEL18")); + jItem = GffReplaceByte(jItem,"Cloth1Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR0")); + jItem = GffReplaceByte(jItem,"Cloth2Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR1")); + jItem = GffReplaceByte(jItem,"Leather1Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR2")); + jItem = GffReplaceByte(jItem,"Leather2Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR3")); + jItem = GffReplaceByte(jItem,"Metal1Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR4")); + jItem = GffReplaceByte(jItem,"Metal2Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR5")); + oItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + int nIndex; + for(nIndex = 0; nIndex <= 18; nIndex++) + { + DeleteLocalInt(oPC, CRAFT_COPY_MODEL + IntToString(nIndex)); + } + for(nIndex = 0; nIndex <= 5; nIndex++) + { + DeleteLocalInt(oPC, CRAFT_COPY_COLOR + IntToString(nIndex)); + } + // Equip new item. + AssignCommand (oTarget, ActionEquipItem (oItem, INVENTORY_SLOT_CHEST)); + } + else if(ai_GetIsShield(oChestItem)) + { + jItem = GffReplaceByte(jItem,"ModelPart1", GetLocalInt(oPC, "CRAFT_COPY_MODEL1")); + jItem = GffReplaceWord(jItem,"xModelPart1", GetLocalInt(oPC, "CRAFT_COPY_MODEL1")); + oItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + // Equip new item. + AssignCommand(oTarget, ActionEquipItem (oItem, INVENTORY_SLOT_LEFTHAND)); + } + else + { + //ai_Debug("pe_crafting", "389", JsonDump(ObjectToJson(oChestItem), 2)); + jItem = GffReplaceByte(jItem,"ModelPart1", GetLocalInt(oPC, "CRAFT_COPY_MODEL0")); + jItem = GffReplaceWord(jItem,"xModelPart1", GetLocalInt(oPC, "CRAFT_COPY_MODEL0")); + jItem = GffReplaceByte(jItem,"Cloth1Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR0")); + jItem = GffReplaceByte(jItem,"Cloth2Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR1")); + jItem = GffReplaceByte(jItem,"Leather1Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR2")); + jItem = GffReplaceByte(jItem,"Leather2Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR3")); + jItem = GffReplaceByte(jItem,"Metal1Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR4")); + jItem = GffReplaceByte(jItem,"Metal2Color", GetLocalInt(oPC, "CRAFT_COPY_COLOR5")); + oItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + DeleteLocalInt(oPC, "CRAFT_COPY_MODEL0"); + int nIndex; + for(nIndex = 0; nIndex <= 5; nIndex++) + { + DeleteLocalInt(oPC, CRAFT_COPY_COLOR + IntToString(nIndex)); + } + // Equip new item. + int nItemType = GetBaseItemType(oChestItem); + if(nItemType == BASE_ITEM_CLOAK) AssignCommand(oTarget, ActionEquipItem (oItem, INVENTORY_SLOT_CLOAK)); + else if(nItemType == BASE_ITEM_HELMET) AssignCommand(oTarget, ActionEquipItem (oItem, INVENTORY_SLOT_HEAD)); + } + // Send message that it has been copied. + AssignCommand(oPC, ai_SendMessages (GetName (oItem) + " appearance has been changed!", AI_COLOR_GREEN, oPC)); + DestroyObject(oChestItem); + return oItem; +} */ +int GetItemSelectedEquipSlot (int nItemSelected) +{ + if (nItemSelected == 0) return INVENTORY_SLOT_CHEST; + if (nItemSelected == 1) return INVENTORY_SLOT_CLOAK; + if (nItemSelected == 2) return INVENTORY_SLOT_HEAD; + if (nItemSelected == 3) return INVENTORY_SLOT_RIGHTHAND; + if (nItemSelected == 4) return INVENTORY_SLOT_LEFTHAND; + return INVENTORY_SLOT_CHEST; +} +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 GetMaxSimpleItemNumber(object oItem, int nBaseItemType) +{ + int nResType, nMaxNumber, nModelNumber; + string sModelNumber, sModelName = Get2DAString("baseitems", "ItemClass", nBaseItemType) + "_"; + //ai_Debug("pe_crafting", "804", "sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber)); + while(nModelNumber < 999) + { + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + if(nBaseItemType == BASE_ITEM_CLOAK) nResType = RESTYPE_PLT; + else nResType = RESTYPE_MDL; + if(ResManGetAliasFor(sModelName + sModelNumber, nResType) != "") nMaxNumber++; + nModelNumber++; + //ai_Debug("pe_crafting", "841", "sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber)); + } + return nMaxNumber; +} +int GetSimpleItemNumber(object oItem, int nModelNumber, int nBaseItemType) +{ + int nResType, nIndex, nCounter; + string sModelNumber, sModelName = Get2DAString("baseitems", "ItemClass", nBaseItemType) + "_"; + //ai_Debug("pe_crafting", "804", "sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber)); + while(nIndex <= 999) + { + if(nIndex < 10) sModelNumber = "00" + IntToString(nIndex); + else if(nIndex < 100) sModelNumber = "0" + IntToString(nIndex); + else sModelNumber = IntToString(nIndex); + if(nBaseItemType == BASE_ITEM_CLOAK) nResType = RESTYPE_PLT; + else nResType = RESTYPE_MDL; + if(ResManGetAliasFor(sModelName + sModelNumber, nResType) != "") nCounter++; + if(nCounter == nModelNumber) return nIndex; + nIndex++; + //ai_Debug("pe_crafting", "841", "sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber)); + } + return nIndex; +} +int GetMaxWeaponModuleNumber(struct stWeaponAppearance stWA) +{ + int nBaseItemType = GetBaseItemType(stWA.oItem); + stWA.nColor = 1; + stWA.nModel = 99; + stWA.sPart = "t"; + string sModelNumber; + string sModelName = Get2DAString("baseitems", "ItemClass", nBaseItemType) + "_" + stWA.sPart + "_"; + int nModelNumber = (stWA.nModel * 10) + stWA.nColor; + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + //SendMessageToPC(GetFirstPC(), "pe_crafting, 780, sModel: " + sModelName + sModelNumber + + // " nModel: " + IntToString(stWA.nModel) + " nColor: " + IntToString(stWA.nColor)); + while(ResManGetAliasFor(sModelName + sModelNumber, RESTYPE_MDL) == "") + { + stWA.nModel += -1; + // Create the model name. + nModelNumber = (stWA.nModel * 10) + stWA.nColor; + if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + //SendMessageToPC(GetFirstPC(), "pe_crafting, 789, sModel: " + sModelName + sModelNumber + + // " nModel: " + IntToString(stWA.nModel) + " nColor: " + IntToString(stWA.nColor)); + } + return stWA.nModel; +} +struct stWeaponAppearance GetNextWeaponAppearance(struct stWeaponAppearance stWA, int nDirection) +{ + int nBaseItemType = GetBaseItemType(stWA.oItem); + string sModelNumber; + string sModelName = Get2DAString("baseitems", "ItemClass", nBaseItemType) + "_" + stWA.sPart + "_"; + // Get next/previous color/model. + stWA.nColor += nDirection; + if(stWA.nColor > 9) + { + stWA.nColor = 1; + stWA.nModel += nDirection; + if(stWA.nModel > CRAFT_MAX_WEAPON_MODEL_NUMBER) stWA.nModel = 1; + } + else if(stWA.nColor < 1) + { + stWA.nColor = 9; + stWA.nModel += nDirection; + if(stWA.nModel < 1) stWA.nModel = CRAFT_MAX_WEAPON_MODEL_NUMBER; + } + int nModelNumber = (stWA.nModel * 10) + stWA.nColor; + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + //SendMessageToPC(GetFirstPC(), "pe_crafting, 778, sModel: " + sModelName + sModelNumber + + // " nModel: " + IntToString(stWA.nModel) + " nColor: " + IntToString(stWA.nColor)); + while(ResManGetAliasFor(sModelName + sModelNumber, RESTYPE_MDL) == "") + { + // Get next/previous color/model. + stWA.nColor += nDirection; + if(stWA.nColor > 9) + { + stWA.nColor = 1; + stWA.nModel += nDirection; + if(stWA.nModel > CRAFT_MAX_WEAPON_MODEL_NUMBER) stWA.nModel = 1; + } + else if(stWA.nColor < 1) + { + stWA.nColor = 9; + stWA.nModel += nDirection; + if(stWA.nModel < 1) stWA.nModel = CRAFT_MAX_WEAPON_MODEL_NUMBER; + } + // Create the model name. + nModelNumber = (stWA.nModel * 10) + stWA.nColor; + if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + //SendMessageToPC(GetFirstPC(), "pe_crafting, 800, sModel: " + sModelName + sModelNumber + + // " nModel: " + IntToString(stWA.nModel) + " nColor: " + IntToString(stWA.nColor)); + } + return stWA; +} +object ChangeItemsAppearance(object oPC, object oTarget, int nToken, object oItem, int nDirection, string sPart) +{ + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + // Get the item we are changing. + int nModelSelected; + int nItemSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + string sModelName, sModelNumber; + object oNewItem; + // Weapons. + if(ai_GetIsWeapon(oItem)) + { + // Freeze animations - vfx 352? + if(sPart == "t") nModelSelected = 2; + else if(sPart == "m") nModelSelected = 1; + else if(sPart == "b") nModelSelected = 0; + sModelName = Get2DAString("baseitems", "ItemClass", GetBaseItemType(oItem)) + "_" + sPart + "_"; + struct stWeaponAppearance stWA; + stWA.oItem = oItem; + stWA.sPart = sPart; + stWA.nModel = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_MODEL, nModelSelected); + stWA.nColor = GetItemAppearance(oItem, ITEM_APPR_TYPE_WEAPON_COLOR, nModelSelected); + stWA = GetNextWeaponAppearance(stWA, nDirection); + json jItem = ObjectToJson(oItem, TRUE); + int nModelNumber = stWA.nModel * 10 + stWA.nColor; + jItem = GffReplaceByte(jItem, "ModelPart" + IntToString(nModelSelected + 1), nModelNumber); + jItem = GffReplaceWord(jItem, "xModelPart" + IntToString(nModelSelected + 1), nModelNumber); + oNewItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + AssignCommand(oTarget, ClearAllActions(TRUE)); + DestroyObject(oItem); + // Item selected 3 is the right hand, 4 is the left hand. + //SendMessageToPC(oPC, "nItemSelected: " + IntToString(nItemSelected)); + if(nItemSelected == 3) + { + AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_RIGHTHAND)); + } + else AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_LEFTHAND)); + NuiSetBind(oPC, nToken, "txt_model_number_" + sPart, JsonString(IntToString(nModelNumber))); + } + // Armor. + else if(nItemSelected == 0) + { + // Create the model name. + // Get the ModelType. + int nAppearance = GetAppearanceType(oTarget); + string sModelName = Get2DAString("appearance", "MODELTYPE", nAppearance); + // Get gender. + if(GetGender(oTarget) == GENDER_MALE) sModelName += "m"; + else sModelName += "f"; + // Get race. + sModelName += Get2DAString("appearance", "RACE", nAppearance); + // Get Phenotype. + sModelName += IntToString(GetPhenoType(oTarget)) + "_"; + // Get the selected model. + nModelSelected = GetArmorModelSelected(oPC); + //ai_Debug("pe_crafting", "646", "nModelSide: " + IntToString(nModelSide)); + // If we are doing the left side (bottom menu options) then add one to + // get the left side. + // Note: Right Thigh and Left Thigh are backwards so this fixes that! + if(sPart == "b") + { + if(nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected--; + else nModelSelected++; + } + int nModelNumber = StringToInt(JsonGetString(NuiGetBind(oPC, nToken, "txt_model_number_" + sPart))); + //int nModelNumber = GetItemAppearance(oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nModelSelected); + //SendMessageToPC(oPC, "pe_crafting, 826, nModelNumber: " + IntToString(nModelNumber) + + // " sPart: " + sPart + " nModelSelected: " + IntToString(nModelSelected)); + int nBaseModelNumber = nModelNumber; + nModelNumber += nDirection; + if(nModelNumber > CRAFT_MAX_MODEL_NUMBER) nModelNumber = 0; + else if(nModelNumber < 0) nModelNumber = CRAFT_MAX_MODEL_NUMBER; + string sModelNumber; + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + // Check for changes to the torso (base part of the armor linked to AC). + if(nModelSelected == ITEM_APPR_ARMOR_MODEL_TORSO) + { + string sCurrentACBonus = Get2DAString("parts_chest", "ACBONUS", nBaseModelNumber); + string sACBonus = Get2DAString ("parts_chest", "ACBONUS", nModelNumber); + sModelName += Get2DAString ("capart", "MDLNAME", nModelSelected); + //SendMessageToPC(oPC, "pe_crafting, 842, sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber) + " sCurrentACBonus: " + sCurrentACBonus + + // " sACBonus: " + sACBonus + " nModelSelected: " + IntToString(nModelSelected)); + while(ResManGetAliasFor(sModelName + sModelNumber, RESTYPE_MDL) == "" || + sACBonus != sCurrentACBonus) + { + nModelNumber += nDirection; + if (nModelNumber > CRAFT_MAX_MODEL_NUMBER) nModelNumber = 0; + else if (nModelNumber < 0) nModelNumber = CRAFT_MAX_MODEL_NUMBER; + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + sACBonus = Get2DAString ("parts_chest", "ACBONUS", nModelNumber); + //SendMessageToPC(oPC, "pe_crafting, 854, sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber) + " sACBonus: " + sACBonus + + // " nModelSelected: " + IntToString(nModelSelected)); + } + // Change the model. + oNewItem = CopyItemAndModify (oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nModelSelected, nModelNumber, TRUE); + DestroyObject (oItem); + AssignCommand (oTarget, ActionEquipItem (oNewItem, INVENTORY_SLOT_CHEST)); + } + // Change all other parts of armor. + else + { + sModelName += Get2DAString("capart", "MDLNAME", nModelSelected); + //SendMessageToPC(oPC, "pe_crafting, 866, sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber) + " nModelSelected: " + IntToString(nModelSelected)); + while(ResManGetAliasFor(sModelName + sModelNumber, RESTYPE_MDL) == "") + { + nModelNumber += nDirection; + if (nModelNumber > CRAFT_MAX_MODEL_NUMBER) nModelNumber = 0; + else if (nModelNumber < 0) nModelNumber = CRAFT_MAX_MODEL_NUMBER; + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + //SendMessageToPC(oPC, "pe_crafting, 705, sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber) + " nModelSelected: " + IntToString(nModelSelected)); + } + oNewItem = CopyItemAndModify (oItem, ITEM_APPR_TYPE_ARMOR_MODEL, nModelSelected, nModelNumber, TRUE); + DestroyObject (oItem); + // If using the linked menu option then change the left side too. + if(sPart == "m" && (nModelSelected != ITEM_APPR_ARMOR_MODEL_NECK && + nModelSelected != ITEM_APPR_ARMOR_MODEL_BELT && + nModelSelected != ITEM_APPR_ARMOR_MODEL_PELVIS && + nModelSelected != ITEM_APPR_ARMOR_MODEL_ROBE)) + { + // Note: Right Thigh and Left Thigh are backwards so this fixes that! + if (nModelSelected == ITEM_APPR_ARMOR_MODEL_RTHIGH) nModelSelected--; + else nModelSelected++; + oItem = CopyItemAndModify(oNewItem, ITEM_APPR_TYPE_ARMOR_MODEL, nModelSelected, nModelNumber, TRUE); + DestroyObject(oNewItem); + AssignCommand(oTarget, ActionEquipItem(oItem, INVENTORY_SLOT_CHEST)); + } + else AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_CHEST)); + } + string sModelSelected; + 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) + { + NuiSetBind(oPC, nToken, "txt_model_number_" + sPart, JsonString(IntToString(nModelNumber))); + } + else + { + if(sPart == "m") + { + // Using labels for Mobile. + //NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString(IntToString(nModelNumber))); + //NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(IntToString(nModelNumber))); + //NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString(IntToString(nModelNumber))); + NuiSetBind(oPC, nToken, "txt_model_number_t", JsonString(IntToString(nModelNumber))); + NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(IntToString(nModelNumber))); + NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString(IntToString(nModelNumber))); + } + else + { + NuiSetBind(oPC, nToken, "txt_model_number_" + sPart, JsonString(IntToString(nModelNumber))); + } + } + } + // All other items. + else + { + int nSlot, nResType, nBaseItemType = GetBaseItemType(oItem); + string sModelName = Get2DAString("baseitems", "ItemClass", nBaseItemType) + "_"; + if(nBaseItemType == BASE_ITEM_CLOAK) + { + nSlot = INVENTORY_SLOT_CLOAK; + nResType = RESTYPE_PLT; + } + else if(nBaseItemType == BASE_ITEM_HELMET) + { + nSlot = INVENTORY_SLOT_HEAD; + nResType = RESTYPE_MDL; + } + else if(nBaseItemType == BASE_ITEM_LARGESHIELD || + nBaseItemType == BASE_ITEM_SMALLSHIELD || + nBaseItemType == BASE_ITEM_TOWERSHIELD) + { + nSlot = INVENTORY_SLOT_LEFTHAND; + nResType = RESTYPE_MDL; + } + int nModelNumber = GetItemAppearance(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0); + nModelNumber += nDirection; + if (nModelNumber > CRAFT_MAX_MODEL_NUMBER) nModelNumber = 0; + else if (nModelNumber < 0) nModelNumber = CRAFT_MAX_MODEL_NUMBER; + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + //ai_Debug("pe_crafting", "804", "sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber)); + while(ResManGetAliasFor(sModelName + sModelNumber, nResType) == "") + { + nModelNumber += nDirection; + if (nModelNumber > CRAFT_MAX_MODEL_NUMBER) nModelNumber = 0; + else if (nModelNumber < 0) nModelNumber = CRAFT_MAX_MODEL_NUMBER; + if(nModelNumber < 10) sModelNumber = "00" + IntToString(nModelNumber); + else if(nModelNumber < 100) sModelNumber = "0" + IntToString(nModelNumber); + else sModelNumber = IntToString(nModelNumber); + //ai_Debug("pe_crafting", "841", "sModelName: " + sModelName + sModelNumber + + // " nModelNumber: " + IntToString(nModelNumber)); + } + oNewItem = CopyItemAndModify(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0, nModelNumber, TRUE); + DestroyObject(oItem); + AssignCommand(oTarget, ActionEquipItem (oNewItem, nSlot)); + NuiSetBind(oPC, nToken, "txt_model_number_" + sPart, JsonString(IntToString(nModelNumber))); + } + return oNewItem; +} +object RandomizeItemsCraftAppearance(object oPC, object oTarget, int nToken, object oItem) +{ + // Get the item we are changing. + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + int nItemSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + int nBaseItemType = GetBaseItemType(oItem); + object oNewItem; + if(ai_GetIsWeapon(oItem)) + { + int nRollTop, nRollMid, nRollBottom; + int nColorTop, nColorMid, nColorBottom; + struct stWeaponAppearance stWA; + stWA.oItem = oItem; + int nMaxModuleNumber = GetMaxWeaponModuleNumber(stWA); + nRollTop = Random(nMaxModuleNumber) + 1; + // Check bows as they must randomize to the same top, middle, and bottom otherwise they look bad. + if(nBaseItemType == BASE_ITEM_LONGBOW || nBaseItemType == BASE_ITEM_SHORTBOW) + { + nRollMid = nRollTop; + nRollBottom = nRollTop; + } + // Randomize each item individualy for other weapons. + else + { + nRollMid = Random(nMaxModuleNumber) + 1; + nRollBottom = Random(nMaxModuleNumber) + 1; + } + nColorTop = Random(9) + 1; + nColorMid = Random(9) + 1; + nColorBottom = Random(9) + 1; + // Change weapons model. + stWA.sPart = "t"; + stWA.nModel = nRollTop; + stWA.nColor = nColorTop; + stWA = GetNextWeaponAppearance(stWA, -1); + json jItem = ObjectToJson(oItem, TRUE); + //ai_Debug("pe_crafting", "614", "ModelPart" + IntToString(nModelSelected + 1) + + // " nModelNumber: " + IntToString(nModelNumber)); + jItem = GffReplaceByte(jItem, "ModelPart" + IntToString(3), stWA.nModel * 10 + stWA.nColor); + jItem = GffReplaceWord(jItem, "xModelPart" + IntToString(3), stWA.nModel * 10 + stWA.nColor); + NuiSetBind(oPC, nToken, "txt_model_number_" + stWA.sPart, JsonString(IntToString(stWA.nModel * 10 + stWA.nColor))); + stWA.sPart = "m"; + stWA.nModel = nRollMid; + stWA.nColor = nColorMid; + stWA = GetNextWeaponAppearance(stWA, -1); + jItem = GffReplaceByte(jItem, "ModelPart" + IntToString(2), stWA.nModel * 10 + stWA.nColor); + jItem = GffReplaceWord(jItem, "xModelPart" + IntToString(2), stWA.nModel * 10 + stWA.nColor); + NuiSetBind(oPC, nToken, "txt_model_number_" + stWA.sPart, JsonString(IntToString(stWA.nModel * 10 + stWA.nColor))); + stWA.sPart = "b"; + stWA.nModel = nRollBottom; + stWA.nColor = nColorBottom; + stWA = GetNextWeaponAppearance(stWA, -1); + jItem = GffReplaceByte(jItem, "ModelPart" + IntToString(1), stWA.nModel * 10 + stWA.nColor); + jItem = GffReplaceWord(jItem, "xModelPart" + IntToString(1), stWA.nModel * 10 + stWA.nColor); + NuiSetBind(oPC, nToken, "txt_model_number_" + stWA.sPart, JsonString(IntToString(stWA.nModel * 10 + stWA.nColor))); + oNewItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + AssignCommand(oTarget, ClearAllActions(TRUE)); + DestroyObject(oItem); + // Item selected 3 is the right hand, 4 is the left hand. + if (nItemSelected == 3) AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_RIGHTHAND)); + else AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_LEFTHAND)); + } + // Armor. + else if(nItemSelected == 0) + { + int nRoll, nRoll2; + json jItem = ObjectToJson(oItem, TRUE); + // Randomize the models. + // Randomize Torso + //jItem = GffReplaceByte(jItem, "ArmorPart_Torso", ); + //jItem = GffReplaceWord(jItem, "xArmorPart_Torso", ); + // Randomize the colors. + nRoll = Random(175) + 1; + if(d100() < 50) nRoll2 = nRoll + Random(5) - 3; + else nRoll2 = Random(175) + 1; + jItem = GffReplaceByte(jItem, "Cloth1Color", nRoll); + jItem = GffReplaceByte(jItem, "Cloth2Color", nRoll2); + if(d100() < 50) nRoll = nRoll + Random(5) - 3; + else nRoll = Random(175) + 1; + if(d100() < 50) nRoll2 = nRoll + Random(5) - 3; + else nRoll2 = Random(175) + 1; + jItem = GffReplaceByte(jItem, "Leather1Color", nRoll); + jItem = GffReplaceByte(jItem, "Leather2Color", nRoll2); + if(d100() < 50) nRoll = nRoll + Random(5) - 3; + else nRoll = Random(175) + 1; + if(d100() < 50) nRoll2 = nRoll + Random(5) - 3; + else nRoll2 = Random(175) + 1; + jItem = GffReplaceByte(jItem, "Metal1Color", nRoll); + jItem = GffReplaceByte(jItem, "Metal2Color", nRoll2); + DestroyObject(oItem); + oNewItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + AssignCommand(oTarget, ActionEquipItem(oNewItem, INVENTORY_SLOT_CHEST)); + } + // All other items. + else + { + int nSlot; + // Get max models and inventory slot. + int nMaxModel = GetMaxSimpleItemNumber(oItem, nBaseItemType); + if(nBaseItemType == BASE_ITEM_CLOAK) nSlot = INVENTORY_SLOT_CLOAK; + else if(nBaseItemType == BASE_ITEM_HELMET) nSlot = INVENTORY_SLOT_HEAD; + else if(nBaseItemType == BASE_ITEM_LARGESHIELD || nBaseItemType == BASE_ITEM_SMALLSHIELD || + nBaseItemType == BASE_ITEM_TOWERSHIELD) nSlot = INVENTORY_SLOT_LEFTHAND; + int nRoll = Random(nMaxModel) + 1; + int nModel = GetSimpleItemNumber(oItem, nRoll, nBaseItemType); + json jItem = ObjectToJson(oItem, TRUE); + jItem = GffReplaceByte(jItem, "ModelPart1", nModel); + jItem = GffReplaceWord(jItem, "xModelPart1", nModel); + if (nBaseItemType == BASE_ITEM_CLOAK || nBaseItemType == BASE_ITEM_HELMET) + { + jItem = GffReplaceByte(jItem, "Cloth1Color", Random(175) + 1); + jItem = GffReplaceByte(jItem, "Cloth2Color", Random(175) + 1); + jItem = GffReplaceByte(jItem, "Leather1Color", Random(175) + 1); + jItem = GffReplaceByte(jItem, "Leather2Color", Random(175) + 1); + jItem = GffReplaceByte(jItem, "Metal1Color", Random(175) + 1); + jItem = GffReplaceByte(jItem, "Metal2Color", Random(175) + 1); + } + DestroyObject(oItem); + oNewItem = JsonToObject(jItem, GetLocation(oTarget), oTarget, TRUE); + AssignCommand(oTarget, ActionEquipItem(oNewItem, nSlot)); + } + return oNewItem; +} +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; +} +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 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); + } +} +// Gets the colorId from a image of the color pallet. +// Thanks Zunath for the base code. +int GetColorPalletId(object oPC, int nToken) +{ + float fScale = IntToFloat(GetPlayerDeviceProperty(oPC, PLAYER_DEVICE_PROPERTY_GUI_SCALE)) / 100.0f; + json jPayload = NuiGetEventPayload(); + json jMousePosition = JsonObjectGet(jPayload, "mouse_pos"); + json jX = JsonObjectGet(jMousePosition, "x"); + json jY = JsonObjectGet(jMousePosition, "y"); + float fX = StringToFloat(JsonDump (jX)); + float fY = StringToFloat(JsonDump (jY)); + float fCellSize = 20.0f * fScale; + int nCellX = FloatToInt(fX / fCellSize); + int nCellY = FloatToInt(fY / fCellSize); + if(nCellX < 0) nCellX = 0; + else if (nCellX > 16) nCellX = 16; + if(nCellY < 0) nCellY = 0; + else if(nCellY > 11) nCellY = 11; + NuiSetBind(oPC, nToken, "color_pallet_pointer", NuiRect(IntToFloat(nCellX * 20), IntToFloat(nCellY * 20), 20.0, 20.0)); + return nCellX + nCellY * 16; +} +void SetColorPalletPointer(object oPC, int nToken, object oItem) +{ + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + 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)); +} +void LockItemInCraftingWindow(object oPC, object oItem, object oTarget, int nToken) +{ + NuiSetBind(oPC, nToken, "item_combo_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_cancel_label", JsonString("Cancel")); + NuiSetBind(oPC, nToken, "btn_cancel_tooltip", JsonString(" Revert back to the original items appearance")); + NuiSetBind(oPC, nToken, "btn_save_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_select_target_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_info_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_wardrobe_event", JsonBool(FALSE)); + // Make sure the item information window is closed. + nToken = NuiFindWindow(oPC, "craft_item_nui"); + if(nToken) NuiDestroy(oPC, nToken); +} +void ClearItemInCraftingWindow(object oPC, object oItem, int nToken) +{ + NuiSetBind(oPC, nToken, "btn_save_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "item_combo_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_select_target_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_info_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_wardrobe_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cancel_label", JsonString("Exit")); + NuiSetBind(oPC, nToken, "btn_cancel_tooltip", JsonString(" Exit the crafting menu")); + if(ai_GetIsWeapon(oItem)) + { + SetLocalInt(oPC, AI_NO_NUI_SAVE, TRUE); + NuiSetBind(oPC, nToken, "model_combo_selected", JsonInt(0)); + DelayCommand(1.0, DeleteLocalInt(oPC, AI_NO_NUI_SAVE)); + } +} +void SaveCraftedItem(object oPC, object oTarget, int nToken) +{ + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + int nItemSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + object oItem = GetSelectedItem(oTarget, nItemSelected); + ClearItemInCraftingWindow(oPC, oItem, nToken); + DestroyObject(GetLocalObject(oPC, CRAFT_ORIGINAL_ITEM)); + DeleteLocalObject(oPC, CRAFT_ORIGINAL_ITEM); +} +int CanCraftItem(object oPC, object oItem, int nToken, int bPasteCheck = FALSE) +{ + // Plot items cannot be changed. + if(GetPlotFlag(oItem)) + { + ai_SendMessages(GetName(oItem) + "is a plot item and its appearance cannot be changed!", AI_COLOR_RED, oPC); + return FALSE; + } + // Cannot change temorary enchanted items. + if(CheckForTemporaryItemProperty(oItem)) + { + ai_SendMessages(GetName(oItem) + " cannot be altered while it has a temporary enchantment.", AI_COLOR_RED, oPC); + return FALSE; + } + // Do special paste checks. + if (bPasteCheck) + { + int nOldItemType = GetLocalInt (oPC, CRAFT_COPY_ITEM_TYPE); + int nNewItemType = GetBaseItemType(oItem); + if(nNewItemType == BASE_ITEM_ARMOR) + { + if(GetLocalInt (oPC, CRAFT_ARMOR_AC) != ai_GetArmorBonus(oItem)) + { + ai_SendMessages("The armor you are trying to paste to is not the same type as the copy!", AI_COLOR_RED, oPC); + return FALSE; + } + } + else if(nOldItemType != nNewItemType) + { + string sOldBaseItem = GetStringByStrRef(StringToInt(Get2DAString ("baseitems", "Name", nOldItemType))); + string sNewBaseItem = GetStringByStrRef(StringToInt(Get2DAString ("baseitems", "Name", nNewItemType))); + ai_SendMessages("You copied a " + sOldBaseItem + " and are trying to paste to a " + sNewBaseItem + "!", AI_COLOR_RED, oPC); + return FALSE; + } + } + if(GetLocalObject(oPC, CRAFT_ORIGINAL_ITEM) == OBJECT_INVALID) + { + object oBuildContainer = GetObjectByTag(CRAFT_CONTAINER); + if(!GetIsObjectValid(oBuildContainer)) + { + vector vPosition = GetPositionFromLocation(GetLocation(oPC)); + vPosition.z = vPosition.z -2.0; + location lLocation = Location(GetArea(oPC), vPosition, 0.0); + oBuildContainer = CreateObject(OBJECT_TYPE_PLACEABLE, CRAFT_TEMPLATE, lLocation, FALSE, CRAFT_CONTAINER); + //SetObjectVisualTransform(oBuildContainer, OBJECT_VISUAL_TRANSFORM_TRANSLATE_Z, -5.0); + } + object oBackup = CopyItem(oItem, oBuildContainer, TRUE); + // Save the original item to the PC. + SetLocalObject(oPC, CRAFT_ORIGINAL_ITEM, oBackup); + } + return TRUE; +} +void RemoveTagedEffects(object oCreature, string sEffectTag) +{ + //Search for the effect. + //Debug ("0i_effects", "578", "RemoveTagedEffects: " + sEffectTag); + effect eEffect = GetFirstEffect(oCreature); + while (GetIsEffectValid(eEffect)) + { + //Debug ("0i_effects", "582", "Effect Tag: " + GetEffectTag (eEffect)); + if (GetEffectTag(eEffect) == sEffectTag) RemoveEffect(oCreature, eEffect); + eEffect = GetNextEffect(oCreature); + } +} +int CheckForTemporaryItemProperty (object oItem) +{ + itemproperty ipProperty; + ipProperty = GetFirstItemProperty (oItem); + while (GetIsItemPropertyValid (ipProperty)) + { + // Check to see if the item is temporary enchanted. + if (GetItemPropertyDurationType (ipProperty) == DURATION_TYPE_TEMPORARY) return TRUE; + ipProperty = GetNextItemProperty (oItem); + } + return FALSE; +} +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")); + //SendMessageToPC(GetFirstPC(), "sPartName: " + sPartName + " nPartColor: " + IntToString(nPartColor)); + return nPartColor; +} +void SetModelNumberText(object oPC, object oTarget, int nToken) +{ + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + int nItem = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + object oItem = GetSelectedItem(oTarget, nItem); + int nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MODEL_SELECTION)); + 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); + NuiSetBind(oPC, nToken, "top_title_label", JsonString("Top")); + //NuiSetBind(oPC, nToken, "txt_model_number_t_enable", JsonBool(TRUE)); + //NuiSetBindWatch(oPC, nToken, "txt_model_number_t", TRUE); + NuiSetBind(oPC, nToken, "txt_model_name_t", JsonString(sModelTop)); + NuiSetBind(oPC, nToken, "btn_prev_t_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_t_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Middle")); + //NuiSetBind(oPC, nToken, "txt_model_number_m_enable", JsonBool(TRUE)); + //NuiSetBindWatch(oPC, nToken, "txt_model_number_m", 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)); + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("Bottom")); + //NuiSetBind(oPC, nToken, "txt_model_number_b_enable", JsonBool(TRUE)); + //NuiSetBindWatch(oPC, nToken, "txt_model_number_b", 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)); + } + // Armor and clothing + 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)); + NuiSetBind(oPC, nToken, "top_title_label", JsonString("")); + //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)); + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Model")); + //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)); + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("")); + //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)); + NuiSetBind(oPC, nToken, "top_title_label", JsonString("Right")); + //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)); + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Right & Left")); + //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)); + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("Left")); + //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)); + } + } + // Cloaks and Helmets. + else + { + sModelMiddle = IntToString(GetItemAppearance(oItem, ITEM_APPR_TYPE_SIMPLE_MODEL, 0)); + NuiSetBind(oPC, nToken, "top_title_label", JsonString("")); + //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)); + //NuiSetBind(oPC, nToken, "txt_model_number_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "txt_model_number_m", JsonString(sModelMiddle)); + NuiSetBind(oPC, nToken, "middle_title_label", JsonString("Model")); + NuiSetBind(oPC, nToken, "btn_prev_m_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_next_m_event", JsonBool(TRUE)); + //NuiSetBind(oPC, nToken, "txt_model_number_b_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "txt_model_number_b", JsonString("")); + NuiSetBind(oPC, nToken, "bottom_title_label", JsonString("")); + NuiSetBind(oPC, nToken, "btn_prev_b_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_next_b_event", JsonBool(FALSE)); + } + // 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"; + 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")); + NuiSetBindWatch(oPC, nToken, "txt_color_l", TRUE); + int nSelectedRight, nSelectedAll, nSelectedLeft; + int nModelSelected = GetArmorModelSelected(oPC); + int nMaterialSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + 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 + nSelectedRight = JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR)); + nSelectedAll = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ALL_COLOR)); + if(!nSelectedRight && !nSelectedAll) + { + nSelectedAll = TRUE; + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonBool(TRUE)); + jCraft = JsonObjectSet(jCraft, CRAFT_LEFT_PART_COLOR, JsonBool(FALSE)); + } + 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 511 + string sColorPallet = GetLocalString(oPC, CRAFT_COLOR_PALLET); + if(sColorPallet == "") sColorPallet = "gui_pal_tattoo"; + 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 + nSelectedRight = JsonGetInt(JsonObjectGet(jCraft, CRAFT_RIGHT_PART_COLOR)); + nSelectedAll = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ALL_COLOR)); + nSelectedLeft = JsonGetInt(JsonObjectGet(jCraft, CRAFT_LEFT_PART_COLOR)); + if(!nSelectedRight && !nSelectedAll && !nSelectedLeft) + { + nSelectedAll = TRUE; + jCraft = JsonObjectSet(jCraft, CRAFT_ALL_COLOR, JsonBool(TRUE)); + } + 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; + //SendMessageToPC(oPC, "nSelectedRight: " + IntToString(nSelectedRight) + + // " nSelectedLeft: " + IntToString(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)); + // Row 516 - Label Material to Color + // Row 517 & 518 + nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + SetMaterialButtons(oPC, nToken, nSelected); + } + SetLocalJson(oPC, CRAFT_JSON, jCraft); + } + // Cloaks and Helmets. + else + { + // Row 512 - Label Part to Color + // Row 5l3 + NuiSetBind(oPC, nToken, "btn_right_part_color", 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", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_left_part_color_event", 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_left_part_reset_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_all_reset_event", JsonBool(FALSE)); + // Row 516 - Label Material to Color + // Row 517 & 518 + nSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_MATERIAL_SELECTION)); + SetMaterialButtons(oPC, nToken, nSelected); + } +} +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)); + } +} +void CreateItemGUIPanel(object oPC, object oItem) +{ + // Row 1 (Name)************************************************************* 73 + json jRow = CreateLabel(JsonArray(), "Name:", "lbl_name_title", 50.0f, 20.0f, NUI_HALIGN_LEFT); + 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)); + // 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"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // 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 + int nBaseItemType = GetBaseItemType(oItem); + float fWeight; + string sBaseItemDesc; + if(nBaseItemType == BASE_ITEM_ARMOR) + { + int nArmorAC = ai_GetArmorBonus(oItem); + sBaseItemDesc = GetStringByStrRef(StringToInt(Get2DAString("armor", "BASEITEMSTATREF", nArmorAC))); + fWeight = StringToFloat(Get2DAString("armor", "WEIGHT", nArmorAC)); + } + else + { + sBaseItemDesc = GetStringByStrRef(StringToInt(Get2DAString("baseitems", "BaseItemStatRef", nBaseItemType))); + fWeight = StringToFloat(Get2DAString("baseitems", "TenthLBS", nBaseItemType)); + } + if(sBaseItemDesc == "Bad Strref") sBaseItemDesc = ""; + if(sBaseItemDesc != "") + { + jRow = CreateTextBox(JsonArray(), "txt_base_desc", 375.0, 150.0, FALSE, NUI_SCROLLBARS_NONE, "txt_base_desc_tooltip"); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + fHeight += 158.0; + } + // Set the layout of the window. + json jLayout = NuiCol (jCol); + object oOwner = GetItemPossessor(oItem); + string sName = ai_StripColorCodes (GetName(oOwner)); + 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); + 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.")); + // 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))); + // Base Item Description + NuiSetBind(oPC, nToken, "txt_base_desc_event", JsonBool(TRUE)); + //NuiSetBind(oPC, nToken, "txt_desc_tooltip", JsonString ("Color codes can be used!")); + if(sBaseItemDesc != "") NuiSetBind(oPC, nToken, "txt_base_desc", JsonString(sBaseItemDesc)); +} +void CraftItemInfoEvents(object oPC, int nToken) +{ + string sEvent = NuiGetEventType(); + // We don't use and it causes error windows to go off! Return early! + if(sEvent == "mouseup") return; + string sElem = NuiGetEventElement(); + int nIndex = NuiGetEventArrayIndex(); + json jCraft = GetLocalJson(oPC, CRAFT_JSON); + //SendMessageToPC(oPC, "0e_crafting, 1961, sElem: " + sElem + " sEvent: " + sEvent); + object oTarget = GetLocalObject(oPC, CRAFT_TARGET); + if(oTarget == OBJECT_INVALID) oTarget = oPC; + // Get the item we are crafting. + int nItemSelected = JsonGetInt(JsonObjectGet(jCraft, CRAFT_ITEM_SELECTION)); + object oItem = GetLocalObject(oPC, "CRAFT_INFO_ITEM"); + if(sEvent == "click") + { + if(sElem == "btn_destroy") + { + if(!JsonGetInt(NuiGetBind(oPC, nToken, "btn_destroy"))) + { + if(!GetPlotFlag(oItem)) + { + DestroyObject(oItem); + ai_SendMessages(GetName(oItem) + " has been permanently destroyed!", AI_COLOR_RED, oPC); + NuiDestroy(oPC, nToken); + } + else + { + ai_SendMessages("The plot flag must be removed before you can destroy " + GetName(oItem) + "!", AI_COLOR_YELLOW, oPC); + } + } + else + { + ai_SendMessages("Click Destroy button again to destroy " + GetName(oItem) + "!", AI_COLOR_RED, oPC); + } + } + // Allows saving the item as a UTI! + else if(sElem == "btn_save_uti") + { + json jItem = ObjectToJson(oItem); + string sResRef = JsonGetString(NuiGetBind(oPC, nToken, "txt_item_resref")); + sResRef = ai_RemoveIllegalCharacters(sResRef); + if(sResRef == "") ai_SendMessages(GetName(oItem) + " has not been saved! ResRef does not have a value.", AI_COLOR_RED, oPC); + else + { + JsonToTemplate(jItem, sResRef, RESTYPE_UTI); + ai_SendMessages(GetName(oItem) + " has been saved as " + sResRef + ".uti in your Neverwinter Nights Temp directory.", AI_COLOR_GREEN, oPC); + ai_SendMessages("This temp directory will be removed when the game is left.", AI_COLOR_GREEN, oPC); + } + } + } + if(sEvent == "watch") + { + // Changing the name needs to be before the cooldown. + if(sElem == "txt_item_name") + { + string sName = JsonGetString(NuiGetBind(oPC, nToken, "txt_item_name")); + SetName(oItem, sName); + int nToken2 = NuiFindWindow(oPC, "crafting_nui"); + if(nToken2) NuiSetBind(oPC, nToken2, "txt_item_name", JsonString(sName)); + } + else if(sElem == "txt_item_tag") + { + string sTag = JsonGetString(NuiGetBind(oPC, nToken, "txt_item_tag")); + SetTag(oItem, sTag); + } + else if(sElem == "txt_stack") + { + int nSize = StringToInt(JsonGetString(NuiGetBind(oPC, nToken, "txt_stack"))); + int nBaseItemType = GetBaseItemType(oItem); + string sMaxSize = Get2DAString("baseitems", "Stacking", nBaseItemType); + if(nSize > StringToInt(sMaxSize)) + { + ai_SendMessages("The maximum stack for this item type is " + sMaxSize + ".", AI_COLOR_RED, oPC); + NuiSetBind(oPC, nToken, "txt_stack", JsonString(sMaxSize)); + } + if(nSize != 0) SetItemStackSize(oItem, nSize); + } + else if(sElem == "txt_charges") + { + int nCharges = StringToInt(JsonGetString(NuiGetBind(oPC, nToken, "txt_charges"))); + if(nCharges > 250) + { + ai_SendMessages("The maximum charges for this item type is 250.", AI_COLOR_RED, oPC); + NuiSetBind(oPC, nToken, "txt_charges", JsonString("250")); + } + if(nCharges != 0) SetItemCharges(oItem, nCharges); + } + else if(sElem == "chbx_plot_check") + { + int nValue = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + SetPlotFlag(oItem, nValue); + } + else if(sElem == "chbx_stolen_check") + { + int nValue = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + SetStolenFlag(oItem, nValue); + } + else if(sElem == "chbx_cursed_check") + { + int nValue = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + SetItemCursedFlag(oItem, nValue); + } + else if(sElem == "chbx_identified_check") + { + int nValue = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + SetIdentified(oItem, nValue); + } + else if(sElem == "chbx_droppable_check") + { + int nValue = JsonGetInt(NuiGetBind(oPC, nToken, sElem)); + SetDroppableFlag(oItem, nValue); + } + } +} +/*void CreateDresserGUIPanel(object oPC, object oTarget) +{ +} + diff --git a/_module/nss/pe_debug.nss b/_module/nss/pe_debug.nss new file mode 100644 index 00000000..5b6ad1a4 --- /dev/null +++ b/_module/nss/pe_debug.nss @@ -0,0 +1,900 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script Name: pe_debug + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + PEPS Plugin to allow use of special Debug scripts +/*////////////////////////////////////////////////////////////////////////////// +#include "0i_main" +#include "0i_module" +#include "0i_menus" +//#include "prc_inc_eventhk" +// Gets a variable from oTarget, if oTarget is OBJECT_INVALID then +// it will get the variable from the Module and Area. +void debug_GetObjectVariable(object oPC, object oTarget, string sDesc = ""); +// Lists the variables from oTarget to the screen. +void debug_ListObjectVariables(object oPC, object oTarget); +// Force event script change to default for oCreature. +void ai_ForceAssociateEventScriptsToDefault(object oPC, object oCreature); +// Reverts event script change from default for oCreature. +void ai_RevertAssociateEventScriptsToDefault(object oPC, object oCreature); +void main() +{ + // Get the last player to use targeting mode + object oPC = GetLastPlayerToSelectTarget(); + string sTargetMode = GetLocalString(oPC, AI_TARGET_MODE); + if(oPC == OBJECT_SELF && sTargetMode != "") + { + // Get the targeting mode data + object oTarget = GetTargetingModeSelectedObject(); + vector vTarget = GetTargetingModeSelectedPosition(); + location lLocation = Location(GetArea(oPC), vTarget, GetFacing(oPC)); + object oObject = GetLocalObject(oPC, "AI_TARGET_OBJECT"); + // If the user manually exited targeting mode without selecting a target, return + if(!GetIsObjectValid(oTarget) && vTarget == Vector()) + { + return; + } + // Targeting code here. + if(sTargetMode == "DEBUG_CREATURE") + { + object oModule = GetModule(); + string sDebugName = GetName(oTarget); + SetLocalString(oModule, AI_RULE_DEBUG_CREATURE, sDebugName); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_RULE_DEBUG_CREATURE, JsonString(sDebugName)); + ai_SetCampaignDbJson("rules", jRules); + SetLocalObject(oPC, "AI_RULE_DEBUG_CREATURE_OBJECT", oTarget); + ExecuteScript("pi_debug", oPC); + } + else if(sTargetMode == "CLEAR_REPUTATION") + { + int nReputation = GetFactionAverageReputation(oTarget, oPC); + object oPCMember = GetFirstFactionMember(oPC, FALSE); + while(GetIsObjectValid(oPCMember)) + { + ClearPersonalReputation(oPCMember, oTarget); + oPCMember = GetNextFactionMember(oPC, FALSE); + } + ai_SendMessages("Your reputation with " + GetName(oTarget) + " has been set to neutral.", AI_COLOR_YELLOW, oPC); + } + else if(sTargetMode == "SET_REPUTATION") + { + 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); + } + else if(sTargetMode == "DEBUG_INFO") + { + ai_SendMessages("Information for " + GetName(oTarget), AI_COLOR_WHITE, oPC); + ai_SendMessages("ResRef: " + GetResRef(oTarget), AI_COLOR_GREEN, oPC); + ai_SendMessages("Tag: " + GetTag(oTarget), AI_COLOR_ORANGE, oPC); + ai_SendMessages("UUID: " + GetObjectUUID(oTarget), AI_COLOR_LIGHT_MAGENTA, oPC); + ai_SendMessages("Faction Commoner: " + IntToString(GetStandardFactionReputation(STANDARD_FACTION_COMMONER, oTarget)), AI_COLOR_GREEN, oPC); + ai_SendMessages("Faction Defender: " + IntToString(GetStandardFactionReputation(STANDARD_FACTION_DEFENDER, oTarget)), AI_COLOR_GREEN, oPC); + ai_SendMessages("Faction Merchant: " + IntToString(GetStandardFactionReputation(STANDARD_FACTION_MERCHANT, oTarget)), AI_COLOR_GREEN, oPC); + ai_SendMessages("Faction Hostile: " + IntToString(GetStandardFactionReputation(STANDARD_FACTION_HOSTILE, oTarget)), AI_COLOR_RED, oPC); + int nObjectType = GetObjectType(oTarget); + if(nObjectType == OBJECT_TYPE_CREATURE) + { + json jObject = ObjectToJson(oTarget); + string sConversation = JsonGetString(GffGetResRef(jObject, "Conversation")); + ai_SendMessages("Conversation: " + sConversation, AI_COLOR_CYAN, oPC); + SendMessageToPC(oPC, "Creature Event Scripts:"); + string sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_HEARTBEAT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_NOTICE); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_NOTICE SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_END_COMBATROUND SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DIALOGUE); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DIALOGUE SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_MELEE_ATTACKED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DAMAGED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DAMAGED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DEATH); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DEATH SCRIPT: " + sScript); + sScript = GetLocalString(oTarget, "AI_ON_DEATH"); + if(sScript != "") + { + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DEATH SECOND SCRIPT: " + sScript); + } + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DISTURBED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DISTURBED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_SPAWN_IN SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_RESTED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_RESTED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_SPELLCASTAT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_BLOCKED_BY_DOOR SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_USER_DEFINED_EVENT SCRIPT: " + sScript); + } + else if(nObjectType == OBJECT_TYPE_DOOR) + { + SendMessageToPC(oPC, "Door Event Scripts:"); + string sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_CLICKED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_CLICKED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_CLOSE); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_CLOSED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_DAMAGE); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DAMAGE SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_DEATH); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DEATH SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_DIALOGUE); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DIALOGUE SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_DISARM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DISARM SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_FAIL_TO_OPEN); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_FAIL_TO_OPEN SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_HEARTBEAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_HEARTBEAT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_LOCK); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_LOCK SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_MELEE_ATTACKED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_MELEE_ATTACKED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_OPEN); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_OPEN SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_SPELLCASTAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_SPELLCASTAT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_TRAPTRIGGERED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_TRAPTRIGGERED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_UNLOCK); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_UNLOCK SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_DOOR_ON_USERDEFINED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_USERDEFINED SCRIPT: " + sScript); + } + else if(nObjectType == OBJECT_TYPE_PLACEABLE) + { + SendMessageToPC(oPC, "Placeable Event Scripts:"); + string sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_CLOSED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_CLOSED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_DAMAGED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DAMAGED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_DEATH); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DEATH SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_DIALOGUE); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DIALOGUE SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_DISARM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DISARM SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_HEARTBEAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_HEARTBEAT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_INVENTORYDISTURBED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_INVENTORYDISTURBED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_LEFT_CLICK); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_LEFT_CLICK SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_LOCK); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_LOCK SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_MELEEATTACKED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_MELEEATTACKED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_OPEN); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_OPEN SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_SPELLCASTAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_SPELLCASTAT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_TRAPTRIGGERED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_TRAPTRIGGERED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_UNLOCK); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_UNLOCK SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_USED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_USED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_USER_DEFINED_EVENT SCRIPT: " + sScript); + } + else if(nObjectType == OBJECT_TYPE_TRIGGER) + { + SendMessageToPC(oPC, "Trigger Event Scripts:"); + string sScript = GetEventScript(oTarget, EVENT_SCRIPT_TRIGGER_ON_CLICKED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_CLICKED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_DISARM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_DISARM SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_HEARTBEAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_HEARTBEAT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_TRIGGER_ON_OBJECT_ENTER); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_OBJECT_ENTER SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_TRIGGER_ON_OBJECT_EXIT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_OBJECT_EXIT SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_PLACEABLE_ON_TRAPTRIGGERED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_TRAPTRIGGERED SCRIPT: " + sScript); + sScript = GetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_USER_DEFINED_EVENT SCRIPT: " + sScript); + } + else + { + // Area event scripts. + object oArea = GetArea(oPC); + SendMessageToPC(oPC, "Area Event Scripts:"); + string sScript = GetEventScript(oArea, EVENT_SCRIPT_AREA_ON_ENTER); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_ENTER SCRIPT: " + sScript); + sScript = GetEventScript(oArea, EVENT_SCRIPT_AREA_ON_EXIT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_EXIT SCRIPT: " + sScript); + sScript = GetEventScript(oArea, EVENT_SCRIPT_PLACEABLE_ON_HEARTBEAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_HEARTBEAT SCRIPT: " + sScript); + sScript = GetEventScript(oArea, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_USER_DEFINED_EVENT SCRIPT: " + sScript); + // Module event scripts. + object oModule = GetModule(); + SendMessageToPC(oPC, GetModuleName() + " Module Event Scripts."); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_ACQUIRE_ITEM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_ACQUIRE_ITEM SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_ACTIVATE_ITEM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_ACTIVATE_ITEM SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_CLIENT_ENTER); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_CLIENT_ENTER SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_CLIENT_EXIT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_CLIENT_EXIT SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_EQUIP_ITEM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_EQUIP_ITEM SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_HEARTBEAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_HEARTBEAT SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_LOSE_ITEM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_LOSE_ITEM SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_MODULE_LOAD); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_MODULE_LOAD SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_MODULE_START); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_MODULE_START SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_NUI_EVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_NUI_EVENT SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_CANCEL_CUTSCENE); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_CANCEL_CUTSCENE SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_CHAT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_CHAT SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_DEATH); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_DEATH SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_DYING); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_DYING SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_GUIEVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_GUIEVENT SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_LEVEL_UP); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_LEVEL_UP SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_REST); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_REST SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_TARGET SCRIPT: " + sScript); + sScript = GetLocalString(oModule, AI_MODULE_TARGET_EVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + if(sScript != "") + { + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_TARGET SECOND SCRIPT: " + sScript); + } + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_TILE_ACTION); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_PLAYER_TILE_ACTION SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_RESPAWN_BUTTON_PRESSED); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_RESPAWN_BUTTON_PRESSED SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_UNEQUIP_ITEM); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_UNEQUIP_ITEM SCRIPT: " + sScript); + sScript = GetEventScript(oModule, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT); + sScript += " [" + ResManGetAliasFor(sScript, RESTYPE_NCS) + "]"; + SendMessageToPC(oPC, "ON_USER_DEFINED_EVENT SCRIPT: " + sScript); + } + /* Checks PRC virtual events. See prc_inc_eventhk + int nIndex = 1; + string sEvent = GetFirstEventScript(oTarget, EVENT_VIRTUAL_ONHEARTBEAT, FALSE); + if(sEvent != "") + { + SendMessageToPC(oPC, "HB event script " + IntToString(nIndex) + ": " + sEvent); + for(nIndex = 2; nIndex < 20; nIndex++) + { + sEvent = GetNextEventScript(oTarget, EVENT_VIRTUAL_ONHEARTBEAT, FALSE); + if(sEvent == "") break; + SendMessageToPC(oPC, "HB event script " + IntToString(nIndex) + ": " + sEvent); + } + }*/ + } + else if(sTargetMode == "SET_NPC_SCRIPTS") + { + if(GetLocalString(oTarget, "AI_ON_HEARTBEAT") == "") + { + ai_ForceAssociateEventScriptsToDefault(oPC, oTarget); + } + else ai_RevertAssociateEventScriptsToDefault(oPC, oTarget); + } + else if(sTargetMode == "CLEAR_CREATURE_EVENTS") + { + ai_SendMessages("Set event scripts for " + GetName(oTarget) + " to default.", AI_COLOR_YELLOW, oPC); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_NOTICE, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DEATH, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_RESTED, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "default"); + SetEventScript(oTarget, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, "default"); + DeleteLocalInt(oTarget, "AI_I_AM_BEING_HEALED"); + DeleteLocalString(oTarget, "AIScript"); + ai_ClearCreatureActions(); + } + else if(sTargetMode == "DEBUG_JSON_DUMP") + { + json jObject = ObjectToJson(oTarget, TRUE); + WriteTimestampedLogEntry(GetName(oTarget) + " JsonDump: " + JsonDump(jObject, 1)); + ai_SendMessages(GetName(oTarget) + " has been dumped to the log file!", AI_COLOR_YELLOW, oPC); + } + else if(sTargetMode == "DEBUG_LIST_VAR") + { + debug_ListObjectVariables(oPC, oTarget); + } + else if(sTargetMode == "DEBUG_SET_VARIABLE") + { + string sVarName = GetLocalString(oPC, "Debug_Var_Name"); + int nVarType = GetLocalInt(oPC, "Debug_Var_Type"); + if(nVarType == 0) // Int + { + string sVarValue = GetLocalString(oPC, "Debug_Var_Value"); + int nVarValue = StringToInt(sVarValue); + SetLocalInt(oTarget, sVarName, nVarValue); + ai_SendMessages(sVarName + " [Int] has been set to " + IntToString(nVarValue) + " for " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + } + else if(nVarType == 1) // Float + { + string sVarValue = GetLocalString(oPC, "Debug_Var_Value"); + DeleteLocalString(oPC, "Debug_Var_Name"); + float fVarValue = StringToFloat(sVarValue); + SetLocalFloat(oTarget, sVarName, fVarValue); + ai_SendMessages(sVarName + " [Float] has been set to " + FloatToString(fVarValue, 0, 2) + " for " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + } + else if(nVarType == 2) // String + { + string sVarValue = GetLocalString(oPC, "Debug_Var_Value"); + SetLocalString(oTarget, sVarName, sVarValue); + ai_SendMessages(sVarName + " [String] has been set to " + sVarValue + " for " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + } + else if(nVarType == 3) // Object + { + // 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. + SetLocalObject(oPC, "AI_TARGET_OBJECT", oTarget); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_SET_OBJECT_VARIABLE"); + ai_SendMessages("Select an object to save to " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | + OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_TRIGGER, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(nVarType == 4) // Location + { + // 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. + SetLocalObject(oPC, "AI_TARGET_OBJECT", oTarget); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_SET_LOCATION_VARIABLE"); + ai_SendMessages("Select a location to save to " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_TILE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + DeleteLocalString(oPC, "Debug_Var_Name"); + DeleteLocalInt(oPC, "Debug_Var_Type"); + DeleteLocalString(oPC, "Debug_Var_Value"); + } + else if(sTargetMode == "DEBUG_SET_OBJECT_VARIABLE") + { + string sVarName = GetLocalString(oPC, "Debug_Var_Name"); + SetLocalObject(oObject, sVarName, oTarget); + DeleteLocalObject(oPC, "AI_TARGET_OBJECT"); + DeleteLocalString(oPC, "Debug_Var_Name"); + ai_SendMessages(sVarName + " [Object] has been set to " + GetName(oObject) + " for " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + } + else if(sTargetMode == "DEBUG_SET_LOCATION_VARIABLE") + { + string sVarName = GetLocalString(oPC, "Debug_Var_Name"); + SetLocalLocation(oObject, sVarName, lLocation); + DeleteLocalObject(oPC, "AI_TARGET_OBJECT"); + DeleteLocalString(oPC, "Debug_Var_Name"); + ai_SendMessages(sVarName + " [Location] has been set to " + LocationToString(lLocation) + " for " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + } + else if(sTargetMode == "DEBUG_DELETE_VARIABLE") + { + string sVarName = GetLocalString(oPC, "Debug_Var_Name"); + int nVarType = GetLocalInt(oPC, "Debug_Var_Type"); + if(nVarType == 0) DeleteLocalInt(oTarget, sVarName); + else if(nVarType == 1) DeleteLocalFloat(oTarget, sVarName); + else if(nVarType == 2) DeleteLocalString(oTarget, sVarName); + else if(nVarType == 4) DeleteLocalObject(oTarget, sVarName); + else if(nVarType == 5) DeleteLocalLocation(oTarget, sVarName); + ai_SendMessages(sVarName + " has been deleted from " + GetName(oTarget), AI_COLOR_YELLOW, oPC); + DeleteLocalString(oPC, "Debug_Var_Name"); + DeleteLocalInt(oPC, "Debug_Var_Type"); + } + else if(sTargetMode == "DEBUG_GET_VARIABLE") + { + debug_GetObjectVariable(oPC, oTarget); + } + } + // Run all non-targeting code here, usually NUI events. + else + { + object oPC = NuiGetEventPlayer(); + int nToken = NuiGetEventWindow(); + string sEvent = NuiGetEventType(); + string sElem = NuiGetEventElement(); + int nIndex = NuiGetEventArrayIndex(); + //string sWndId = NuiGetWindowId(oPC, nToken); + //********************************************************************** + //if(GetLocalInt(oPC, AI_NO_NUI_SAVE)) return; + if(sEvent == "click") + { + if(sElem == "btn_npc_scripts") + { + // 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_NPC_SCRIPTS"); + NuiDestroy(oPC, nToken); + 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") + { + // 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"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select a creature to set all standard reputations to neutral.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_clear_reputation") + { + // 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, "CLEAR_REPUTATION"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select a creature to clear your PC's reputation with that creature's faction.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_info") + { + // 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, "DEBUG_INFO"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select an object to send it's information to the players screen.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL , MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_obj_json") + { + // 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, "DEBUG_JSON_DUMP"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select an object to dump it's json values to the log.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | + OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_TRIGGER, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_obj_var") + { + // 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, "DEBUG_LIST_VAR"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select an object to list it's variables to the player screen.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_debug_creature") + { + // 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. + SetLocalObject(oPC, AI_TARGET_ASSOCIATE, OBJECT_SELF); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_CREATURE"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select a creature to start sending debug information to the log for.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_clear_events") + { + // 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. + SetLocalObject(oPC, AI_TARGET_ASSOCIATE, OBJECT_SELF); + SetLocalString(oPC, AI_TARGET_MODE, "CLEAR_CREATURE_EVENTS"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select a creature to set event scripts to default.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_clear_debug") + { + object oModule = GetModule(); + SetLocalString(oModule, AI_RULE_DEBUG_CREATURE, ""); + json jRules = ai_GetCampaignDbJson("rules"); + jRules = JsonObjectSet(jRules, AI_RULE_DEBUG_CREATURE, JsonString("")); + ai_SetCampaignDbJson("rules", jRules); + DeleteLocalObject(oPC, "AI_RULE_DEBUG_CREATURE_OBJECT"); + ai_SendMessages("Creature Debug mode has been cleared.", AI_COLOR_YELLOW, oPC); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_debug", oPC); + } + else if(sElem == "btn_delete_var") + { + // 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. + string sVarName = JsonGetString(NuiGetBind(oPC, nToken, "txt_var_name")); + SetLocalString(oPC, "Debug_Var_Name", sVarName); + SetLocalString(oPC, "Debug_Var_Value", JsonGetString(NuiGetBind(oPC, nToken, "txt_var_value"))); + SetLocalInt(oPC, "Debug_Var_Type", JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected"))); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_DELETE_VARIABLE"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select Object to delete (" + sVarName + ") variable from.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_get_var") + { + // 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. + string sVarName = JsonGetString(NuiGetBind(oPC, nToken, "txt_var_name")); + SetLocalString(oPC, "Debug_Var_Name", sVarName); + SetLocalString(oPC, "Debug_Var_Value", JsonGetString(NuiGetBind(oPC, nToken, "txt_var_value"))); + SetLocalInt(oPC, "Debug_Var_Type", JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected"))); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_GET_VARIABLE"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select Object to get (" + sVarName + ") variable from.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(sElem == "btn_set_var") + { + // 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. + string sVarName = JsonGetString(NuiGetBind(oPC, nToken, "txt_var_name")); + SetLocalString(oPC, "Debug_Var_Name", sVarName); + SetLocalString(oPC, "Debug_Var_Value", JsonGetString(NuiGetBind(oPC, nToken, "txt_var_value"))); + SetLocalInt(oPC, "Debug_Var_Type", JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected"))); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_SET_VARIABLE"); + NuiDestroy(oPC, nToken); + ai_SendMessages("Select Object to set (" + sVarName + ") variable to.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + } + if(sEvent == "watch") + { + if(sElem == "txt_var_name" || sElem == "txt_var_value" || + sElem == "cmb_var_type_selected") + { + if(JsonGetString(NuiGetBind(oPC, nToken, "txt_var_name")) != "") + { + NuiSetBind(oPC, nToken, "btn_get_var_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_delete_var_event", JsonBool(TRUE)); + if(JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected")) == 3 || // Objects + JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected")) == 4 || // Locations + JsonGetString(NuiGetBind(oPC, nToken, "txt_var_value")) != "") + { + NuiSetBind(oPC, nToken, "btn_set_var_event", JsonBool(TRUE)); + return; + } + } + else + { + NuiSetBind(oPC, nToken, "btn_get_var_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_delete_var_event", JsonBool(FALSE)); + } + NuiSetBind(oPC, nToken, "btn_set_var_event", JsonBool(FALSE)); + } + } + if(sEvent == "mousedown") + { + int nMouseButton = JsonGetInt(JsonObjectGet(NuiGetEventPayload(), "mouse_btn")); + if(nMouseButton == NUI_MOUSE_BUTTON_RIGHT) + { + if(sElem == "btn_delete_var") + { + object oModule = GetModule(); + // Set Targeting variables. + string sVarName = JsonGetString(NuiGetBind(oPC, nToken, "txt_var_name")); + int nVarType = JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected")); + if(nVarType == 0) DeleteLocalInt(oModule, sVarName); + else if(nVarType == 1) DeleteLocalFloat(oModule, sVarName); + else if(nVarType == 2) DeleteLocalString(oModule, sVarName); + else if(nVarType == 4) DeleteLocalObject(oModule, sVarName); + else if(nVarType == 5) DeleteLocalLocation(oModule, sVarName); + ai_SendMessages(sVarName + " has been deleted from the Module", AI_COLOR_YELLOW, oPC); + } + else if(sElem == "btn_get_var") + { + // Set Targeting variables. + SetLocalString(oPC, "Debug_Var_Name", JsonGetString(NuiGetBind(oPC, nToken, "txt_var_name"))); + SetLocalInt(oPC, "Debug_Var_Type", JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected"))); + debug_GetObjectVariable(oPC, GetModule(), "(Module)"); + } + else if(sElem == "btn_set_var") + { + // Set Targeting variables. + string sVarName = JsonGetString(NuiGetBind(oPC, nToken, "txt_var_name")); + string sVarValue = JsonGetString(NuiGetBind(oPC, nToken, "txt_var_value")); + int nVarType = JsonGetInt(NuiGetBind (oPC, nToken, "cmb_var_type_selected")); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_SET_VARIABLE"); + if(nVarType == 0) // Int + { + int nVarValue = StringToInt(sVarValue); + SetLocalInt(GetModule(), sVarName, nVarValue); + ai_SendMessages(sVarName + " [Int] has been set to " + IntToString(nVarValue) + " on the Module.", AI_COLOR_YELLOW, oPC); + } + else if(nVarType == 1) // Float + { + float fVarValue = StringToFloat(sVarValue); + SetLocalFloat(GetModule(), sVarName, fVarValue); + ai_SendMessages(sVarName + " [Float] has been set to " + FloatToString(fVarValue, 0, 2) + " on the Module.", AI_COLOR_YELLOW, oPC); + } + else if(nVarType == 2) // String + { + SetLocalString(GetModule(), sVarName, sVarValue); + ai_SendMessages(sVarName + " [String] has been set to " + sVarValue + " on the Module.", AI_COLOR_YELLOW, oPC); + } + else if(nVarType == 3) // Object + { + object oModule = GetModule(); + // 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, "Debug_Var_Name", sVarName); + SetLocalObject(oPC, "AI_TARGET_OBJECT", oModule); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_SET_OBJECT_VARIABLE"); + ai_SendMessages("Select an object to save to " + GetName(oModule), AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | + OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_TRIGGER, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + else if(nVarType == 4) // Location + { + object oModule = GetModule(); + // 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, "Debug_Var_Name", sVarName); + SetLocalObject(oPC, "AI_TARGET_OBJECT", oModule); + SetLocalString(oPC, AI_TARGET_MODE, "DEBUG_SET_LOCATION_VARIABLE"); + ai_SendMessages("Select a location to save to " + GetName(oModule), AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_TILE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE); + } + } + } + } + } +} +void debug_GetObjectVariable(object oPC, object oTarget, string sDesc = "") +{ + string sVar, sVarName = GetLocalString(oPC, "Debug_Var_Name"); + int nVarType = GetLocalInt(oPC, "Debug_Var_Type"); + if(nVarType == 0) sVar = IntToString(GetLocalInt(oTarget, sVarName)); + else if(nVarType == 1) sVar = FloatToString(GetLocalFloat(oTarget, sVarName), 0, 2); + else if(nVarType == 2) sVar = GetLocalString(oTarget, sVarName); + else if(nVarType == 4) sVar = GetName(GetLocalObject(oTarget, sVarName)); + else if(nVarType == 5) sVar = LocationToString(GetLocalLocation(oTarget, sVarName)); + ai_SendMessages(sVarName + " on " + GetName(oTarget) + sDesc + " is set to " + sVar, AI_COLOR_YELLOW, oPC); +} +void debug_ListObjectVariables(object oPC, object oTarget) +{ + string sName = GetName(oTarget); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + ai_SendMessages(sName + " variables:", AI_COLOR_GREEN, oPC); + json jObject = ObjectToJson(oTarget, TRUE); + json jVarTable = GffGetList(jObject, "VarTable"); + string sVariable; + int nIndex, nVarType; + json jVar = JsonArrayGet(jVarTable, nIndex); + while(JsonGetType(jVar) != JSON_TYPE_NULL) + { + sVariable = JsonGetString(GffGetString(jVar, "Name")); + nVarType = JsonGetInt(GffGetDword(jVar, "Type")); + if(nVarType == 1) + { + sVariable += " [int] "; + sVariable += IntToString(JsonGetInt(GffGetInt(jVar, "Value"))); + } + else if(nVarType == 2) + { + sVariable += " [float] "; + sVariable += FloatToString(JsonGetFloat(GffGetFloat(jVar, "Value")), 0, 2); + } + else if(nVarType == 3) + { + sVariable += " [string] "; + sVariable += JsonGetString(GffGetString(jVar, "Value")); + } + else if(nVarType == 4) + { + sName = GetName(GetLocalObject(oTarget, sVariable)); + sVariable += " [object] " + sName; + } + else if(nVarType == 5) + { + sName = LocationToString(GetLocalLocation(oTarget, sVariable)); + sVariable += " [location] " + sName; + } + else if(nVarType == 7) + { + sVariable += " [struct] "; + sVariable += JsonDump(GffGetStruct(jVar, "Value")); + } + sVariable += JsonGetString(JsonObjectGet(jVar, "Value")); + ai_SendMessages(sVariable, AI_COLOR_YELLOW, oPC); + jVar = JsonArrayGet(jVarTable, ++nIndex); + } + if(!nIndex) ai_SendMessages("No variables to list!", AI_COLOR_YELLOW, oPC); +} +void ai_ForceAssociateEventScriptsToDefault(object oPC, object oCreature) +{ + ai_SendMessages("Changing " + GetName(oCreature) + "'s event scripts to default event scripts!", AI_COLOR_YELLOW, oPC); + ai_SendMessages("Use this tool on them again to revert this creatures event scripts back!", AI_COLOR_YELLOW, oPC); + //********** On Heartbeat ********** + string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); + if(sScript == "0e_id_events" || sScript == "0e_prc_id_events") + { + ai_SendMessages("You cannot use this on creatures in Infinite Dungeons!"); + return; + } + SetLocalString(oCreature, "AI_ON_HEARTBEAT", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "nw_ch_ac1"); + //********** On Perception ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE); + SetLocalString(oCreature, "AI_ON_NOTICE", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, "nw_ch_ac2"); + //********** On End Combat Round ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND); + SetLocalString(oCreature, "AI_ON_END_COMBATROUND", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "nw_ch_ac3"); + //********** On Dialogue ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE); + SetLocalString(oCreature, "AI_ON_DIALOGUE", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "nw_ch_ac4"); + //********** On Melee Attacked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED); + SetLocalString(oCreature, "AI_ON_MELEE_ATTACKED", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "nw_ch_ac5"); + //********** On Damaged ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED); + SetLocalString(oCreature, "AI_ON_DAMAGED", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "nw_ch_ac6"); + //********** On Disturbed ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED); + SetLocalString(oCreature, "AI_ON_DISTURBED", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "nw_ch_ac8"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //********** On Rested ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED); + SetLocalString(oCreature, "AI_ON_RESTED", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, "nw_ch_aca"); + //********** On Spell Cast At ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT); + SetLocalString(oCreature, "AI_ON_SPELLCASTAT", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "nw_ch_acb"); + //********** On Blocked ********** + sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR); + SetLocalString(oCreature, "AI_ON_BLOCKED_BY_DOOR", sScript); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "nw_ch_acb"); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); + if(!GetCommandable(oCreature)) SetCommandable(TRUE, oCreature); +} +void ai_RevertAssociateEventScriptsToDefault(object oPC, object oCreature) +{ + ai_SendMessages("Changing " + GetName(oCreature) + "'s event scripts back to original!", AI_COLOR_YELLOW, oPC); + //********** On Heartbeat ********** + string sScript = GetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); + if(sScript == "0e_id_events" || sScript == "0e_prc_id_events") + { + ai_SendMessages("You cannot use this on creatures in Infinite Dungeons!", AI_COLOR_RED, oPC); + return; + } + sScript = GetLocalString(oCreature, "AI_ON_HEARTBEAT"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, sScript); + //********** On Perception ********** + sScript = GetLocalString(oCreature, "AI_ON_NOTICE"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_NOTICE, sScript); + //********** On End Combat Round ********** + sScript = GetLocalString(oCreature, "AI_ON_END_COMBATROUND"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, sScript); + //********** On Dialogue ********** + sScript = GetLocalString(oCreature, "AI_ON_DIALOGUE"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, sScript); + //********** On Melee Attacked ********** + sScript = GetLocalString(oCreature, "AI_ON_MELEE_ATTACKED"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, sScript); + //********** On Damaged ********** + sScript = GetLocalString(oCreature, "AI_ON_DAMAGED"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, sScript); + //********** On Disturbed ********** + sScript = GetLocalString(oCreature, "AI_ON_DISTURBED"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, sScript); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, ""); + //********** On Rested ********** + sScript = GetLocalString(oCreature, "AI_ON_RESTED"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_RESTED, sScript); + //********** On Spell Cast At ********** + sScript = GetLocalString(oCreature, "AI_ON_SPELLCASTAT"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, sScript); + //********** On Blocked ********** + sScript = GetLocalString(oCreature, "AI_ON_BLOCKED_BY_DOOR"); + SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, sScript); + //SetEventScript(oCreature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, ""); + if(!GetCommandable(oCreature)) SetCommandable(TRUE, oCreature); +} + diff --git a/_module/nss/pe_henchmen.nss b/_module/nss/pe_henchmen.nss new file mode 100644 index 00000000..4f6bf42f --- /dev/null +++ b/_module/nss/pe_henchmen.nss @@ -0,0 +1,589 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pe_henchmen +//////////////////////////////////////////////////////////////////////////////// + Used with pe_henchmen to run the npc plugin for + Philos Single Player Enhancements. +*/////////////////////////////////////////////////////////////////////////////// +#include "pinc_henchmen" +#include "x0_i0_henchman" +#include "0i_module" +// Creates the Henchman widget. +void PopupWidgetHenchmanGUIPanel(object oPC); +void ResetHenchmanWindows(object oPC, int nToken, object oHenchman) +{ + DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "henchman_nui"))); + DelayCommand(0.1, ExecuteScript("pi_henchmen", oPC)); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.2, CreateCharacterEditGUIPanel(oPC, oHenchman)); +} +void main() +{ + //************************************************************************** + //********************** Henchmen Targeting Execution ********************** + //************************************************************************** + // Get the last player to use targeting mode + object oPC = GetLastPlayerToSelectTarget(); + if(GetLocalInt (oPC, "0_No_Win_Save")) return; + string sTargetMode = GetLocalString(oPC, AI_TARGET_MODE); + if(oPC == OBJECT_SELF && sTargetMode != "") + { + // Get the targeting mode data + object oTarget = GetTargetingModeSelectedObject(); + vector vTarget = GetTargetingModeSelectedPosition(); + location lLocation = Location(GetArea(oPC), vTarget, GetFacing(oPC)); + object oObject = GetLocalObject(oPC, "AI_TARGET_OBJECT"); + // If the user manually exited targeting mode without selecting a target, return + if(!GetIsObjectValid(oTarget) && vTarget == Vector()) + { + return; + } + // Targeting code here. + if(sTargetMode == "MAKE_NPC_HENCHMAN") + { + if(GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN) + { + ai_SendMessages(GetName(oTarget) + " is already a henchman!", AI_COLOR_RED, oPC); + return; + } + oTarget = CopyObject(oTarget, GetLocation(oPC), OBJECT_INVALID, "", TRUE); + ai_ClearCombatState(oTarget); + ChangeToStandardFaction(oTarget, STANDARD_FACTION_DEFENDER); + DeleteLocalInt(oTarget, AI_ONSPAWN_EVENT); + ai_ChangeEventScriptsForAssociate(oTarget); + AddHenchman(oPC, oTarget); + // Special check for Infinite Dungeon plot givers to be changed into henchman. + if(GetStringLeft(GetLocalString(oTarget, "sConversation"), 8) == "id1_plot") + { + DeleteLocalString(oTarget, "sConversation"); + } + // Remove this variable so they may get a unique tag associate widget. + DeleteLocalString(oTarget, AI_TAG); + ai_SendMessages(GetName(oTarget) + " has been copied and is now in your party as a henchman.", AI_COLOR_GREEN, oPC); + //ExecuteScript("pi_henchmen", oPC); + } + } + //************************************************************************** + //*********************** Henchmen Elements Execution ********************** + //************************************************************************** + else + { + // Let the inspector handle what it wants. + //HandleWindowInspectorEvent (); + object oPC = NuiGetEventPlayer(); + int nToken = NuiGetEventWindow(); + string sEvent = NuiGetEventType(); + string sElem = NuiGetEventElement(); + int nIndex = NuiGetEventArrayIndex(); + string sWndId = NuiGetWindowId (oPC, nToken); + //SendMessageToPC(oPC, "pe_henchmen , 26 sWndId: " + sWndId + " sEvent: " + sEvent + " sElem: " + sElem + + // " nToken: " + IntToString(nToken) + " nIndex: " + IntToString(nIndex) + + // " oPC: " + GetName(oPC)); + //********************************************************************** + // Watch to see if the window moves and save. + if(sElem == "window_geometry" && sEvent == "watch") + { + if(GetLocalInt(oPC, "AI_NO_NUI_SAVE")) return; + json jGeometry = NuiGetBind(oPC, nToken, "window_geometry"); + json jData = GetHenchmanDbJson(oPC, "henchman", "0"); + if(JsonGetType(jData) == JSON_TYPE_NULL) jData = JsonObject(); + jData = JsonObjectSet(jData, sWndId, jGeometry); + SetHenchmanDbJson(oPC, "henchman", jData, "0"); + } + else if(sWndId == "henchman_nui") + { + //********************************************************************** + // Henchman menu. + if(sEvent == "click") + { + string sParty = GetHenchmanDbString(oPC, "henchname", "0"); + // Change to a different saved party #. + if(GetStringLeft(sElem, 9) == "btn_party") + { + sParty = GetStringRight(sElem, 1); + SetHenchmanDbString(oPC, "henchname", sParty, "0"); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ExecuteScript("pi_henchmen", oPC)); + } + // Add an NPC in the game as a henchman. + else if(sElem == "btn_npc_henchman") + { + // Set this variable on the player so PEPS can run the targeting script for this plugin. + SetLocalString(oPC, AI_PLUGIN_TARGET_SCRIPT, "pe_henchmen"); + // Set Targeting variables. + SetLocalString(oPC, AI_TARGET_MODE, "MAKE_NPC_HENCHMAN"); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + ai_SendMessages("Select an NPC to copy and make your henchman.", AI_COLOR_YELLOW, oPC); + EnterTargetingMode(oPC, OBJECT_TYPE_ALL , MOUSECURSOR_CREATE, MOUSECURSOR_NOCREATE); + } + // ******************* Saved Character buttons ********************* + // Show saved party member. + else if(sElem == "btn_saved_char") + { + string sIndex = IntToString(nIndex); + SetHenchmanDbString(oPC, "henchname", sIndex, sParty); + AddSavedCharacterInfo(oPC, nToken, sParty); + } + // Have any saved henchman not in the party join. + else if(sElem == "btn_join_party") + { + SavedPartyJoin(oPC, nToken, sParty); + } + else if(sElem == "btn_saved_join") + { + SavedCharacterJoin(oPC, nToken, sParty); + } + else if(sElem == "btn_saved_remove") + { + string sIndex = GetHenchmanDbString(oPC, "henchname", sParty); + RemoveHenchmanDb(oPC, sParty + sIndex); + if(GetHenchmanDbString(oPC, "henchname", sParty + "0") == "") + { + SetHenchmanDbString(oPC, "henchname", "", sParty); + } + else SetHenchmanDbString(oPC, "henchname", "0", sParty); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ExecuteScript("pi_henchmen", oPC)); + } + else if(sElem == "btn_clear_party") + { + SavedPartyCleared(oPC, nToken, sParty); + } + // ******************* Current Character buttons ********************* + // Show current party member. + else if(sElem == "btn_cur_char") + { + string sIndex = IntToString(nIndex); + SetHenchmanDbString(oPC, "image", sIndex, sParty); + AddCurrentCharacterInfo(oPC, nToken, sParty); + } + // The edit button, for now we are using it to level up! + else if(sElem == "btn_cur_edit") + { + object oHenchman = GetSelectedHenchman(oPC, sParty); + SetLocalObject(oPC, HENCHMAN_TO_EDIT, oHenchman); + CreateCharacterEditGUIPanel(oPC, oHenchman); + } + else if(sElem == "btn_cur_remove") + { + RemoveYourHenchman(oPC, nToken, sParty); + } + else if(sElem == "btn_remove_party") + { + RemoveWholeParty(oPC, nToken, sParty); + } + else if(sElem == "btn_cur_save") + { + SaveYourHenchman(oPC, nToken, sParty); + SetHenchmanDbString(oPC, "henchname", "0", sParty); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, ExecuteScript("pi_henchmen", oPC)); + } + else if(sElem == "btn_save_party") + { + SaveWholeParty(oPC, nToken, sParty); + } + } + /*else if(sEvent == "watch") + { + if(sElem == "henchman_widget_check") + { + int bWidget = JsonGetInt(NuiGetBind(oPC, nToken, "henchman_widget_check")); + SetLocalInt(oPC, "AI_WIDGET_HENCHMAN", bWidget); + if(bWidget) PopupWidgetHenchmanGUIPanel(oPC); + else DelayCommand(0.0, NuiDestroy(oPC, NuiFindWindow(oPC, "widgethenchmanwin"))); + } + if(sElem == "lock_henchman_widget_check") + { + int bBuffLockWidget = JsonGetInt(NuiGetBind(oPC, nToken, "lock_henchman_widget_check")); + SetLocalInt(oPC, "AI_WIDGET_HENCHMAN_LOCK", bBuffLockWidget); + SetLocalInt(oPC, "AI_WIDGET_HENCHMAN", TRUE); + NuiSetBind(oPC, nToken, "henchman_widget_check", JsonBool(TRUE)); + PopupWidgetHenchmanGUIPanel(oPC); + } + } + //************************************************************************** + // Spell Buffing. + else if (sWndId == "widget_henchman") + { + if (sEvent == "click") + { + string sParty; + if (sElem == "btn_one") sParty = "1"; + if (sElem == "btn_two") sParty = "2"; + if (sElem == "btn_three") sParty = "3"; + if (sElem == "btn_four") sParty = "4"; + SetHenchmanDbString (oPC, "henchname", sParty, "0"); + PopupWidgetHenchmanGUIPanel(oPC); + } + } */ + } + else if(sWndId == "henchman_edit_nui") + { + int nChange = 0; + int nID; + string sResRef, sID, sPlot; + object oHenchman = GetLocalObject(oPC, HENCHMAN_TO_EDIT); + if(sEvent == "watch") + { + if(sElem == "char_name") + { + string sName = JsonGetString(NuiGetBind(oPC, nToken, "char_name")); + SetName(oHenchman, sName); + } + if(sElem == "port_name") + { + if(GetLocalInt(oPC, "AI_PORTRAIT_ID_SET")) + { + DeleteLocalInt(oPC, "AI_PORTRAIT_ID_SET"); + //nID = JsonGetInt(NuiGetUserData(oPC, nToken)); + //SetPortraitId(oHenchman, nID); + } + else NuiSetUserData(oPC, nToken, JsonInt(-1)); + sResRef = JsonGetString (NuiGetBind(oPC, nToken, "port_name")); + if(ResManGetAliasFor(sResRef + "l", RESTYPE_TGA) == "" && + ResManGetAliasFor(sResRef + "l", RESTYPE_DDS) == "") + { + if(GetGender(oHenchman)) sResRef = "po_hu_f_99_"; + else sResRef = "po_hu_m_99_"; + } + NuiSetBind (oPC, nToken, "port_resref_image", JsonString (sResRef + "l")); + } + else if(sElem == "cmb_class_selected") + { + int nPosition = JsonGetInt(NuiGetBind(oPC, nToken, "opt_classes_value")) + 1; + int nSelection = JsonGetInt(NuiGetBind(oPC, nToken, "cmb_class_selected")); + int nClass = GetClassBySelection2DA(nSelection); + SetLocalInt(oHenchman, "CLASS_SELECTED_" + IntToString(nPosition), nClass); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, CreateCharacterEditGUIPanel(oPC, oHenchman)); + } + else if(sElem == "cmb_package_selected") + { + int nPosition = JsonGetInt(NuiGetBind(oPC, nToken, "opt_classes_value")) + 1; + string sClass = IntToString(GetLocalInt(oHenchman, "CLASS_SELECTED_" + IntToString(nPosition))); + int nSelection = JsonGetInt(NuiGetBind(oPC, nToken, "cmb_package_selected")); + int nPackage = GetPackageBySelection2DA(sClass, nSelection); + SetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nPosition), nPackage); + } + else if(sElem == "cmb_soundset_selected") + { + int nSelection = JsonGetInt(NuiGetBind(oPC, nToken, "cmb_soundset_selected")); + int nSoundSet = GetSoundSetBySelection2DA(oHenchman, nSelection); + SetSoundset(oHenchman, nSoundSet); + string sResRef = GetStringLowerCase(Get2DAString("soundset", "RESREF", nSoundSet)); + if(GetStringLeft(sResRef, 4) == "vs_f") + { + DelayCommand(0.1, ai_HaveCreatureSpeak(oHenchman, 11, ":1:2:3:22:34:35:41:42:44:45:46:")); + } + else if(GetStringLeft(sResRef, 4) == "vs_n") + { + DelayCommand(0.1, ai_HaveCreatureSpeak(oHenchman, 10, ":1:2:3:34:35:36:40:42:44:45:")); + } + else + { + DelayCommand(0.1, ai_HaveCreatureSpeak(oHenchman, 7, ":1:2:3:11:12:13:33:")); + } + } + } + if(sEvent == "click") + { + if (sElem == "btn_desc_save") + { + string sDescription = JsonGetString(NuiGetBind(oPC, nToken, "desc_value")); + SetDescription(oHenchman, sDescription); + return; + } + else if(sElem == "btn_level_up") + { + int nPosition = JsonGetInt(NuiGetBind(oPC, nToken, "opt_classes_value")) + 1; + int nClass = GetClassByPosition(nPosition, oHenchman); + if(nClass == CLASS_TYPE_INVALID) + { + nClass = GetLocalInt(oHenchman, "CLASS_SELECTED_" + IntToString(nPosition)); + int nIndex = 1; + while(nIndex < 5) + { + if(nClass == GetClassByPosition(nIndex, oHenchman)) + { + ai_SendMessages(GetName(oHenchman) + " already has this class in a different slot! You can only level up this class in its original slot.", AI_COLOR_RED, oPC); + return; + } + nIndex++; + } + } + int nPackage = GetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nPosition)); + if(nPackage == 0) nPackage = GetPackageBySelection2DA(IntToString(nClass), 0); + else if(nPackage == -1) + { + ai_SendMessages("There is not a valid package for this class!", AI_COLOR_RED, oPC); + return; + } + string sLevel = IntToString(GetLevelByClass(nClass, oHenchman) + 1); + json jHenchman = ObjectToJson(oHenchman, TRUE); + //WriteTimestampedLogEntry("pe_henchmen, 318, jHenchman: " + JsonDump(jHenchman, 4)); + // Check to see if this character has a LvlStatList that is required to level. + json jLvlStatList = JsonObjectGet(jHenchman, "LvlStatList"); + //WriteTimestampedLogEntry("pe_henchmen, 321, jLvlStatList: " + JsonDump(jLvlStatList, 4)); + if(JsonGetType(jLvlStatList) == JSON_TYPE_NULL) + { + RemoveHenchman(oPC, oHenchman); + // Make sure to get a clean faction version of the henchman here. + jHenchman = ObjectToJson(oHenchman, TRUE); + jHenchman = CreateLevelStatList(jHenchman, oHenchman, oPC); + location lLocation = GetLocation(oHenchman); + int nFamiliar, nCompanion; + object oCompanion = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oHenchman); + if(oCompanion != OBJECT_INVALID) nFamiliar = TRUE; + oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oHenchman); + if(oCompanion != OBJECT_INVALID) nCompanion = TRUE; + AssignCommand(oHenchman, SetIsDestroyable(TRUE, FALSE, FALSE)); + DestroyObject(oHenchman); + oHenchman = ai_AddHenchman(oPC, jHenchman, lLocation, nFamiliar, nCompanion); + SetLocalObject(oPC, HENCHMAN_TO_EDIT, oHenchman); + // We need to move party button list index to the last one since + // the henchman will move to the last henchman slot. + int nIndex = 1; + object oHench = GetHenchman(oPC, nIndex); + while(oHench != OBJECT_INVALID) + { + oHench = GetHenchman(oPC, ++nIndex); + //SendMessageToPC(oPC, "oHench: " + GetName(oHench) + " nIndex: " + IntToString(nIndex)); + } + string sParty = GetHenchmanDbString(oPC, "henchname", "0"); + SetHenchmanDbString(oPC, "image", IntToString(nIndex - 1), sParty); + } + int nLeveled = LevelUpHenchman(oHenchman, nClass, TRUE, nPackage); + //SendMessageToPC(oPC, "pe_henchmen, 282, nClass: " + IntToString(nClass) + + // " nPackage: " + IntToString(nPackage) + " nPosition: " + IntToString(nPosition) + + // " nLeveled: " + IntToString(nLeveled)); + string sClass = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + if(!nLeveled) + { + //WriteTimestampedLogEntry("pe_henchmen, 306, jLvlStatList: " + JsonDump(jLvlStatList, 1)); + ai_SendMessages(GetName(oHenchman) + " could not level " + sClass + " to level " + sLevel + "!", AI_COLOR_RED, oPC); + } + else + { + ai_SendMessages(GetName(oHenchman) + " has leveled " + sClass + " to " + sLevel + " level!", AI_COLOR_GREEN, oPC); + ResetHenchmanWindows(oPC, nToken, oHenchman); + } + return; + } + else if(sElem == "btn_reset") + { + oHenchman = ResetCharacter(oPC, oHenchman); + SetLocalObject(oPC, HENCHMAN_TO_EDIT, oHenchman); + ai_SendMessages(GetName(oHenchman) + " has been reset to level 1!", AI_COLOR_GREEN, oPC); + // We need to move party button list index to the last one since + // the henchman will move to the last henchman slot. + int nIndex = 1; + object oHench = GetHenchman(oPC, nIndex); + while(oHench != OBJECT_INVALID) + { + oHench = GetHenchman(oPC, ++nIndex); + } + string sParty = GetHenchmanDbString(oPC, "henchname", "0"); + SetHenchmanDbString(oPC, "image", IntToString(nIndex - 1), sParty); + ResetHenchmanWindows(oPC, nToken, oHenchman); + } + else if(sElem == "btn_portrait_next") + { + nID = JsonGetInt(NuiGetUserData(oPC, nToken)) + 1; + nChange = 1; + } + else if(sElem == "btn_portrait_prev") + { + nID = JsonGetInt(NuiGetUserData(oPC, nToken)) - 1; + nChange = -1; + } + else if(sElem == "btn_portrait_ok") + { + nID = JsonGetInt(NuiGetUserData(oPC, nToken)); + if(nID != -1) SetPortraitId(oHenchman, nID); + else + { + sResRef = JsonGetString (NuiGetBind (oPC, nToken, "port_name")); + if(ResManGetAliasFor(sResRef + "l", RESTYPE_TGA) == "" && + ResManGetAliasFor(sResRef + "l", RESTYPE_DDS) == "") + { + if(GetGender(oHenchman)) sResRef = "po_hu_f_99_"; + else sResRef = "po_hu_m_99_"; + SetPortraitResRef(oHenchman, sResRef); + } + } + int nHenchToken = NuiFindWindow(oPC, "henchman_nui"); + if(nHenchToken) + { + string sImage = GetPortraitResRef(oHenchman); + NuiSetBind(oPC, nHenchToken, "img_cur_portrait_image", JsonString(sImage + "l")); + } + } + if (nChange != 0) + { + int nPRace, nPGender; + int nMax2DARow = Get2DARowCount("portraits") - 1; + if(nID > 5000) nID = 1; + if(nID < 0) nID = 5000; + int nGender = GetGender(oHenchman); + int nRace = GetRacialType(oHenchman); + string sPRace = Get2DAString("portraits", "Race", nID); + if(sPRace != "") nPRace = StringToInt(sPRace); + else nPRace = -1; + string sResRef, sPGender = Get2DAString("portraits", "Sex", nID); + if(sPGender != "") nPGender = StringToInt(sPGender); + else nPGender = -1; + //WriteTimestampedLogEntry("pe_henchmen, 367, nGender: " + IntToString(nGender) + + // " nPGender: " + IntToString(nPGender) + + // " nRace: " + IntToString(nRace) + " nPRace: " + IntToString(nPRace) + + // " nID: " + IntToString(nID)); + while((nRace != nPRace && + (nRace != RACIAL_TYPE_HALFELF || + (nPRace != RACIAL_TYPE_ELF || nPRace != RACIAL_TYPE_HUMAN))) || + nGender != nPGender && nPGender != 4) + { + nID += nChange; + //WriteTimestampedLogEntry("pe_henchmen, 382, nCounter: " + IntToString(nCounter) + + // " nMax2DARow: " + IntToString(nMax2DARow)); + if (nID > 5000) nID = 1; + if (nID < 1) nID = 5000; + sPRace = Get2DAString("portraits", "Race", nID); + if(sPRace != "") nPRace = StringToInt(sPRace); + else nPRace = -1; + sPGender = Get2DAString("portraits", "Sex", nID); + if(sPGender != "") nPGender = StringToInt(sPGender); + else nPGender = -1; + //WriteTimestampedLogEntry("pe_henchmen, 385, nGender: " + IntToString(nGender) + + // " nPGender: " + IntToString(nPGender) + " sPGender: " + sPGender + + // " nRace: " + IntToString(nRace) + " nPRace: " + IntToString(nPRace) + + // " sPRace: " + sPRace + " nID: " + IntToString(nID)); + sResRef = "po_" + Get2DAString("portraits", "BaseResRef", nID) + "l"; + if(ResManGetAliasFor(sResRef, RESTYPE_TGA) == "" && + ResManGetAliasFor(sResRef, RESTYPE_DDS) == "") nPRace = 99; + } + sResRef = "po_" + Get2DAString("portraits", "BaseResRef", nID); + NuiSetUserData(oPC, nToken, JsonInt (nID)); + // This is passed to the portrait name txt that actually sets + // the portrait information and tells it we picked an ID. + SetLocalInt(oPC, "AI_PORTRAIT_ID_SET", TRUE); + NuiSetBind(oPC, nToken, "port_name", JsonString (sResRef)); + } + } + if(sEvent == "mousedown") + { + int nMouseButton = JsonGetInt(JsonObjectGet(NuiGetEventPayload(), "mouse_btn")); + if (sElem == "opt_classes" && nMouseButton == NUI_MOUSE_BUTTON_LEFT) + { + int nPosition = JsonGetInt(NuiGetBind(oPC, nToken, "opt_classes_value")); + SetLocalInt(oHenchman, "CLASS_OPTION_POSITION", nPosition); + DelayCommand(0.0, NuiDestroy(oPC, nToken)); + DelayCommand(0.1, CreateCharacterEditGUIPanel(oPC, oHenchman)); + return; + } + if(nMouseButton == NUI_MOUSE_BUTTON_RIGHT) + { + if(sElem == "cmb_class") + { + int nPosition = JsonGetInt(NuiGetBind(oPC, nToken, "opt_classes_value")) + 1; + int nClass = GetLocalInt(oHenchman, "CLASS_SELECTED_" + IntToString(nPosition)); + string sName = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + string sDescription = GetStringByStrRef(StringToInt(Get2DAString("classes", "Description", nClass))); + string sIcon = Get2DAString("classes", "Icon", nClass); + CreateCharacterDescriptionNUI(oPC, sName, sIcon, sDescription); + } + else if(sElem == "cmb_package") + { + int nPosition = JsonGetInt(NuiGetBind(oPC, nToken, "opt_classes_value")) + 1; + int nClass = GetLocalInt(oHenchman, "CLASS_SELECTED_" + IntToString(nPosition)); + int nPackage = GetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nPosition)); + string sName = GetStringByStrRef(StringToInt(Get2DAString("packages", "Name", nPackage))); + string sDescription = GetStringByStrRef(StringToInt(Get2DAString("packages", "Description", nPackage))); + string sIcon = Get2DAString("classes", "Icon", nClass); + CreateCharacterDescriptionNUI(oPC, sName, sIcon, sDescription); + } + else if(sElem == "cmb_soundset") + { + int nSelection = JsonGetInt(NuiGetBind(oPC, nToken, "cmb_soundset_selected")); + int nSoundSet = GetSoundSetBySelection2DA(oHenchman, nSelection); + string sResRef = GetStringLowerCase(Get2DAString("soundset", "RESREF", nSoundSet)); + if(GetStringLeft(sResRef, 4) == "vs_f") + { + DelayCommand(0.1, ai_HaveCreatureSpeak(oHenchman, 11, ":1:2:3:22:34:35:41:42:44:45:46:")); + } + else if(GetStringLeft(sResRef, 4) == "vs_n") + { + DelayCommand(0.1, ai_HaveCreatureSpeak(oHenchman, 10, ":1:2:3:34:35:36:40:42:44:45:")); + } + else + { + DelayCommand(0.1, ai_HaveCreatureSpeak(oHenchman, 7, ":1:2:3:11:12:13:33:")); + } + } + else if(sElem == "opt_classes") + { + int nPosition = JsonGetInt(NuiGetBind(oPC, nToken, "opt_classes_value")) + 1; + int nClass = GetClassByPosition(nPosition, oHenchman); + if(nClass != CLASS_TYPE_INVALID) + { + string sName = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + string sDescription = GetStringByStrRef(StringToInt(Get2DAString("classes", "Description", nClass))); + int nPackage = GetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nPosition)); + string sPackageName = GetStringByStrRef(StringToInt(Get2DAString("packages", "Name", nPackage))); + sDescription += "\n\nPACKAGE: \n" + sPackageName + "\n"; + sDescription += GetStringByStrRef(StringToInt(Get2DAString("packages", "Description", nPackage))); + string sIcon = Get2DAString("classes", "Icon", nClass); + CreateCharacterDescriptionNUI(oPC, sName, sIcon, sDescription); + } + } + } + } + } + else if(sWndId == "char_description_nui") + { + if(sEvent == "click" && sElem == "btn_ok") DelayCommand(0.0, NuiDestroy(oPC, nToken)); + } + } +} +void PopupWidgetHenchmanGUIPanel(object oPC) +{ + // 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")); + // Row 1 (buttons)********************************************************** + json jRow = CreateButtonImage(JsonArray(), "ir_level1", "btn_one", 30.0f, 30.0f); + jRow = CreateButtonImage(jRow, "ir_level2", "btn_two", 30.0f, 30.0f); + jRow = CreateButtonImage(jRow, "ir_level3", "btn_three", 30.0f, 30.0f); + jRow = CreateButtonImage(jRow, "ir_level4", "btn_four", 30.0f, 30.0f); + // Add the row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + int bAINPCWidgetLock = GetLocalInt(oPC, "AI_WIDGET_HENCHMAN_LOCK"); + // Get the window location to restore it from the database. + float fX = GetLocalFloat(oPC, "widget_henchman_X"); + float fY = GetLocalFloat(oPC, "widget_henchman_Y"); + if(fX == 0.0f && fY == 0.0f) + { + fX = 10.0f; + fY = 10.0f; + } + if(bAINPCWidgetLock) + { + fX = fX + 4.0f; + fY = fY + 45.0f; + } + // Set the layout of the window. + json jLayout = NuiCol (jCol); + int nToken; + if(bAINPCWidgetLock) nToken = SetWindow (oPC, jLayout, "widget_henchman", "Henchman Widget", fX, fY, 160.0, 62.0, FALSE, FALSE, FALSE, TRUE, FALSE, "pe_npc"); + else nToken = SetWindow (oPC, jLayout, "widget_henchman", "Henchman Widget", fX, fY, 160.0, 95.0, FALSE, FALSE, FALSE, TRUE, TRUE, "pe_npc"); + // 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)); + 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/_module/nss/pi_buffing.nss b/_module/nss/pi_buffing.nss new file mode 100644 index 00000000..6ce454ea --- /dev/null +++ b/_module/nss/pi_buffing.nss @@ -0,0 +1,338 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pi_buffing +//////////////////////////////////////////////////////////////////////////////// + Executable plug in script for Philos Module Extentions. + + Database structure: + Name(string) Tag(String) Spells(Json) + Tag: Widget - 0 = x position, 1 = y position, 2 = On/Off, 3 = Locked + Tag: List (string) set to the list number selected 1,2,3, or 4. + Tag: List# is the list of spells for List number 1,2,3, or 4. + + UI to save a players buff spells to be cast after resting. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" +const int BUFF_MAX_SPELLS = 50; +const string FB_NO_MONSTER_CHECK = "FB_NO_MONSTER_CHECK"; + +// Does startup check if the game has just been loaded. +int StartingUp(object oPC); +// Creates the table and initializes if it needs to. +void CheckBuffDataAndInitialize(object oPlayer, string sTag); +// sDataField should be one of the data fields for that table. +// sData is the string data to be saved. +void SetBuffDatabaseString(object oPlayer, string sDataField, string sData, string sTag); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +string GetBuffDatabaseString(object oPlayer, string sDataField, string sTag); +// sDataField should be one of the data fields for that table. +// jData is the json data to be saved. +void SetBuffDatabaseJson(object oPlayer, string sDataField, json jData, string sTag); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +json GetBuffDatabaseJson(object oPlayer, string sDataField, string sTag); +// Creates the widget for buffing. +void PopupWidgetBuffGUIPanel(object oPC); + +void main() +{ + object oPC = OBJECT_SELF; + // Check to make sure the database is setup before we do anything. + CheckBuffDataAndInitialize(oPC, "menudata"); + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + if(JsonGetType(JsonArrayGet(jMenuData, 0)) == JSON_TYPE_NULL) + { + jMenuData = JsonArrayInsert(JsonArray(), JsonString("list1")); // 0 Spell List # + jMenuData = JsonArrayInsert(jMenuData, JsonFloat(0.0)); // 1 Main menu X pos. + jMenuData = JsonArrayInsert(jMenuData, JsonFloat(GetGUIHeightMiddle(oPC, 257.0))); // 2 Main menu Y pos. + jMenuData = JsonArrayInsert(jMenuData, JsonBool(FALSE)); // 3 Widget on/off + jMenuData = JsonArrayInsert(jMenuData, JsonBool(FALSE)); // 4 Widget Locked + jMenuData = JsonArrayInsert(jMenuData, JsonFloat(10.0)); // 5 Widget X pos. + jMenuData = JsonArrayInsert(jMenuData, JsonFloat(10.0)); // 6 Widget Y pos. + SetBuffDatabaseJson(oPC, "spells", jMenuData, "menudata"); + } + 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); + // 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) + { + 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)); + // Row 4 (List of Spells) ************************************************** 164 + // Create the button template for the List. + jRow = JsonArray(); + string sList = JsonGetString(JsonArrayGet(jMenuData, 0)); + int nCntr, nIndex; + string sCntr, sIndex; + json jSpell; + CheckBuffDataAndInitialize(oPC, sList); + json jSpells = GetBuffDatabaseJson(oPC, "spells", sList); + while(nCntr <= BUFF_MAX_SPELLS) + { + jSpell = JsonArrayGet(jSpells, nCntr); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + sIndex = IntToString(nIndex++); + jRow = CreateButtonImage(jRow, "", "btn_spell_" + sIndex, 35.0, 35.0, 0.0, "btn_spell_" + sIndex + "_tooltip"); + } + nCntr++; + } + // Add the row to the column. + 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; + float fX = JsonGetFloat(JsonArrayGet(jMenuData, 1)); + float fY = JsonGetFloat(JsonArrayGet(jMenuData, 2)); + if(fX == 0.0f && fY == 0.0f) + { + fX = 0.0f; + fY = GetGUIHeightMiddle(oPC, 257.0); + } + // Set the layout of the window. + 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 the elements to show events. + int nSelected = GetEventScript(oPC, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT) == "pc_savebuffs"; + NuiSetBind(oPC, nToken, "btn_save", JsonBool(nSelected)); + NuiSetBind(oPC, nToken, "btn_save_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_save_tooltip", JsonString(" Saves any spells cast on you or your associates.")); + NuiSetBind(oPC, nToken, "btn_clear", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_tooltip", JsonString(" Clears the current list of all saved spells.")); + NuiSetBind(oPC, nToken, "btn_buff", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_buff_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_buff_tooltip", JsonString(" Casts the current list of saved spells.")); + if(sList == "list1") NuiSetBind (oPC, nToken, "btn_list1", JsonBool (TRUE)); + else NuiSetBind(oPC, nToken, "btn_list1", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_list1_event", JsonBool(TRUE)); + if(sList == "list2") NuiSetBind (oPC, nToken, "btn_list2", JsonBool (TRUE)); + else NuiSetBind(oPC, nToken, "btn_list2", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_list2_event", JsonBool(TRUE)); + if(sList == "list3") NuiSetBind (oPC, nToken, "btn_list3", JsonBool (TRUE)); + else NuiSetBind(oPC, nToken, "btn_list3", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_list3_event", JsonBool(TRUE)); + 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)); + 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."; + NuiSetBind(oPC, nToken, "buff_widget_tooltip", JsonString(sText)); + nValue = JsonGetInt(JsonArrayGet(jMenuData, 4)); + NuiSetBind(oPC, nToken, "lock_buff_widget_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "lock_buff_widget_check", JsonBool(nValue)); + 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) + { + NuiSetBind(oPC, nToken, "chbx_no_monster_check_event", JsonBool(TRUE)); + nValue = GetLocalInt(oPC, FB_NO_MONSTER_CHECK); + NuiSetBind(oPC, nToken, "chbx_no_monster_check_check", JsonBool(nValue)); + NuiSetBindWatch(oPC, nToken, "chbx_no_monster_check_check", TRUE); + sText = " Turns on/off checks for nearby monsters."; + NuiSetBind(oPC, nToken, "chbx_no_monster_check_tooltip", JsonString(sText)); + } + // Create buttons with spells listed. + int nSpell, nClass, nLevel, nMetamagic, nDomain; + string sName, sTargetName, sResRef; + nCntr = 0; + nIndex = 0; + while(nCntr <= BUFF_MAX_SPELLS) + { + jSpell = JsonArrayGet(jSpells, nCntr); + if(JsonGetType(jSpell) != JSON_TYPE_NULL) + { + nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); + nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); + nLevel = JsonGetInt(JsonArrayGet(jSpell, 2)); + nMetamagic = JsonGetInt(JsonArrayGet(jSpell, 3)); + nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); + sTargetName = JsonGetString(JsonArrayGet(jSpell, 5)); + sResRef = Get2DAString("spells", "IconResRef", nSpell); + sName = " " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); + sName += " (" + GetStringByStrRef(StringToInt(Get2DAString("classes", "Short", nClass))); + sName += " / " + IntToString (nLevel); + 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"; + } + if(nDomain > 0) sName += " / Domain"; + sName += ") " + sTargetName; + sIndex = IntToString(nIndex++); + NuiSetBind(oPC, nToken, "btn_spell_" + sIndex + "_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_spell_" + sIndex + "_image", JsonString(sResRef)); + NuiSetBind(oPC, nToken, "btn_spell_" + sIndex + "_tooltip", JsonString(sName)); + } + nCntr++; + } + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); +} +int StartingUp(object oPC) +{ + if(GetLocalInt(oPC, AI_ADD_PLUGIN)) + { + json jPlugin = JsonArray(); + jPlugin = JsonArrayInsert(jPlugin, JsonString("pi_buffing")); + jPlugin = JsonArrayInsert(jPlugin, JsonInt(FALSE)); + jPlugin = JsonArrayInsert(jPlugin, JsonString("Quick Buff")); + jPlugin = JsonArrayInsert(jPlugin, JsonString("dm_appear")); + json jPlugins = GetLocalJson(oPC, AI_JSON_PLUGINS); + jPlugins = JsonArrayInsert(jPlugins, jPlugin); + SetLocalJson(oPC, AI_JSON_PLUGINS, jPlugin); + SetLocalInt(oPC, AI_PLUGIN_SET, TRUE); + return TRUE; + } + if(!GetLocalInt(oPC, AI_STARTING_UP)) return FALSE; + json jMenuData = GetBuffDatabaseJson(oPC, "spells", "menudata"); + int bWidgetOn = JsonGetInt(JsonArrayGet(jMenuData, 3)); + if(bWidgetOn) + { + PopupWidgetBuffGUIPanel(oPC); + ai_SendMessages("Buffing widget has been created.", AI_COLOR_YELLOW, oPC); + } + return TRUE; +} +void CreateBuffDataTable(object oPlayer) +{ + sqlquery sql = SqlPrepareQueryObject(oPlayer, + "CREATE TABLE IF NOT EXISTS BUFF_TABLE (" + + "name TEXT, " + + "tag TEXT, " + + "spells TEXT, " + + "PRIMARY KEY(name, tag));"); + SqlStep(sql); +} +void CheckBuffDataAndInitialize(object oPlayer, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "SELECT name FROM sqlite_master WHERE type ='table' AND name=@tableName;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString (sql, "@tableName", "BUFF_TABLE"); + if(!SqlStep (sql)) CreateBuffDataTable(oPlayer); + sQuery = "SELECT name FROM BUFF_TABLE Where name = @name AND tag = @tag;"; + sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + if(!SqlStep(sql)) + { + sQuery = "INSERT INTO BUFF_TABLE(name, tag, spells) " + + "VALUES (@name, @tag, @spells);"; + sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + SqlBindJson(sql, "@spells", JsonArray()); + SqlStep(sql); + } +} +void SetBuffDatabaseString(object oPlayer, string sDataField, string sData, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "UPDATE BUFF_TABLE SET " + sDataField + " = @data WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@data", sData); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + SqlStep(sql); +} +string GetBuffDatabaseString(object oPlayer, string sDataField, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "SELECT " + sDataField + " FROM BUFF_TABLE WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + if(SqlStep(sql)) return SqlGetString(sql, 0); + else return ""; +} +void SetBuffDatabaseJson (object oPlayer, string sDataField, json jData, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "UPDATE BUFF_TABLE SET " + sDataField + " = @data WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindJson(sql, "@data", jData); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + SqlStep(sql); +} +json GetBuffDatabaseJson(object oPlayer, string sDataField, string sTag) +{ + string sName = ai_RemoveIllegalCharacters(ai_StripColorCodes(GetName(oPlayer, TRUE))); + string sQuery = "SELECT " + sDataField + " FROM BUFF_TABLE WHERE name = @name AND tag = @tag;"; + sqlquery sql = SqlPrepareQueryObject(oPlayer, sQuery); + SqlBindString(sql, "@name", sName); + SqlBindString(sql, "@tag", sTag); + if(SqlStep(sql)) return SqlGetJson(sql, 0); + else return JsonArray(); +} +void PopupWidgetBuffGUIPanel(object oPC) +{ + // 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)); + // 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); + // Add the row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + json jWidget = GetBuffDatabaseJson(oPC, "spells", "menudata"); + int bAIBuffWidgetLock = JsonGetInt(JsonArrayGet(jWidget, 4)); + // Get the window location to restore it from the database. + float fX = JsonGetFloat(JsonArrayGet(jWidget, 5)); + float fY = JsonGetFloat(JsonArrayGet(jWidget, 6)); + if(fX == 0.0f && fY == 0.0f) + { + fX = 10.0f; + fY = 10.0f; + } + if(bAIBuffWidgetLock) + { + fX = fX + 4.0f; + fY = fY + 45.0f; + } + // Set the layout of the window. + 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"); + // 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)); + 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/_module/nss/pi_crafting.nss b/_module/nss/pi_crafting.nss new file mode 100644 index 00000000..b7d6309b --- /dev/null +++ b/_module/nss/pi_crafting.nss @@ -0,0 +1,718 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pi_crafting +//////////////////////////////////////////////////////////////////////////////// + Executable plug in script for Philos Module Extentions + + Crafting UI for players items. +*/////////////////////////////////////////////////////////////////////////////// +#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); + 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(); + 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,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(), "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; +} +int StartingUp(object oPC) +{ + if(GetLocalInt(oPC, AI_ADD_PLUGIN)) + { + json jPlugin = JsonArray(); + jPlugin = JsonArrayInsert(jPlugin, JsonString("pi_crafting")); + jPlugin = JsonArrayInsert(jPlugin, JsonInt(FALSE)); + jPlugin = JsonArrayInsert(jPlugin, JsonString("Item Crafting")); + jPlugin = JsonArrayInsert(jPlugin, JsonString("isk_x2cweap")); + json jPlugins = GetLocalJson(oPC, AI_JSON_PLUGINS); + jPlugins = JsonArrayInsert(jPlugins, jPlugin); + SetLocalJson(oPC, AI_JSON_PLUGINS, jPlugin); + SetLocalInt(oPC, AI_PLUGIN_SET, TRUE); + return TRUE; + } + if(!GetLocalInt(oPC, AI_STARTING_UP)) return FALSE; + return TRUE; +} + diff --git a/_module/nss/pi_debug.nss b/_module/nss/pi_debug.nss new file mode 100644 index 00000000..6555882a --- /dev/null +++ b/_module/nss/pi_debug.nss @@ -0,0 +1,200 @@ +/*////////////////////////////////////////////////////////////////////////////// + Script: pi_debug + Programmer: Philos +//////////////////////////////////////////////////////////////////////////////// + Plugin for debugging. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" +#include "0i_player_target" +// Does startup check if the game has just been loaded. +int StartingUp(object oPC); +void main() +{ + object oPC = OBJECT_SELF; + if(StartingUp(oPC)) return; + // 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)); + string sText = " [Single player]"; + if(AI_SERVER) sText = " [Server]"; + // ************************************************************************* Width / Height + // Row 1 ******************************************************************* 500 / 73 + json jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, PHILOS_VERSION + sText, "lbl_version", 470.0f, 20.0f, NUI_HALIGN_CENTER); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 ******************************************************************* 500 / 129 + sText = "Module: " + GetModuleName() + " [" + GetTag(GetModule()) + "]"; + jRow = CreateLabel(JsonArray(), sText, "lbl_module_name", 470.0f, 20.0f, NUI_HALIGN_CENTER); + // 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)); + // 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)); + // 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)); + // 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 = 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()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 7 ******************************************************************* 500 / 269 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "Display Target Info", "btn_info", 150.0f, 20.0f, -1.0, "btn_info_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Dump Object to Json", "btn_obj_json", 150.0f, 20.0f, -1.0, "btn_obj_json_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "List Object Variables", "btn_obj_var", 150.0f, 20.0f, -1.0, "btn_obj_var_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 8 ******************************************************************* 500 / 297 jRow = JsonArray(); + jRow = CreateButton(JsonArray(), "Delete Variable", "btn_delete_var", 115.0f, 25.0f, -1.0, "btn_delete_var_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Set Variable", "btn_set_var", 115.0f, 25.0f, -1.0, "btn_set_var_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Get Variable", "btn_get_var", 115.0f, 25.0f, -1.0, "btn_get_var_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + json jCombo = JsonArrayInsert(JsonArray(), NuiComboEntry("int", 0)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("float", 1)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("string", 2)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("object", 3)); + jCombo = JsonArrayInsert(jCombo, NuiComboEntry("location", 4)); + jRow = CreateCombo(jRow, jCombo, "cmb_var_type", 115.0, 25.0); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 9 ******************************************************************* 500 / 329 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, "Name:", "lbl_name", 40.0f, 20.0f); + jRow = CreateTextEditBox(jRow, "sPlaceHolder", "txt_var_name", 40, FALSE, 425.0f, 20.0f, "txt_var_name_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 10 ******************************************************************* 500 / 357 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateLabel(jRow, "Value:", "lbl_value", 40.0f, 20.0f); + jRow = CreateTextEditBox(jRow, "sPlaceHolder", "txt_var_value", 40, FALSE, 425.0f, 20.0f, "txt_var_value_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 11 ******************************************************************* 500 / 385 + // Make the debug creature group. + // Group Row 1 ******************************************************************* 500 / 385 + json jGroupRow = CreateButton(JsonArray(), "Debug Creature", "btn_debug_creature", 120.0f, 20.0f, -1.0, "btn_debug_creature_tooltip"); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButton(jGroupRow, "Clear Event Scripts", "btn_clear_events", 150.0f, 20.0f, -1.0, "btn_clear_events_tooltip"); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupRow = CreateButton(jGroupRow, "Clear Debug", "btn_clear_debug", 120.0f, 20.0f, -1.0, "btn_clear_debug_tooltip"); + // Add group row to the group column. + json jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + float fHeight = 431.0; + // Group Row 2 ******************************************************************* 500 / --- + object oDebugCreature = GetLocalObject(oPC, "AI_RULE_DEBUG_CREATURE_OBJECT"); + if(GetIsObjectValid(oDebugCreature)) + { + string sScript = GetEventScript(oDebugCreature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT); + if(sScript == "nw_c2_default1") sText = GetName(oDebugCreature) + " is using monster AI scripts (" + sScript + ")."; + else if(sScript == "nw_ch_ac1") sText = GetName(oDebugCreature) + " is using associate AI scripts (" + sScript + ")."; + else if(sScript == "xx_pc_1_hb") sText = GetName(oDebugCreature) + " is using player AI scripts (" + sScript + ")."; + else if(sScript == "0e_id_events") sText = GetName(oDebugCreature) + " is using Infinite Dungeons AI scripts (" + sScript + ")."; + else if(sScript == "0e_prc_id_events") sText = GetName(oDebugCreature) + " is using PRC Infinite Dungeons AI scripts (" + sScript + ")."; + else sText = GetName(oDebugCreature) + " is using unknown AI scripts (" + sScript + ")."; + jGroupRow = CreateLabel(JsonArray(), sText, "debug_info", 455.0f, 20.0f, NUI_HALIGN_CENTER); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight = fHeight + 28; + } + // Group Row 3 ******************************************************************* 500 / --- + sText = GetLocalString(GetModule(), AI_RULE_DEBUG_CREATURE); + if(sText != "") sText = sText + " is sending AI debug to the log file."; + else sText = "Nothing is sending AI debug to the log file."; + jGroupRow = CreateLabel(JsonArray(), sText, "debug_log", 455.0f, 20.0f, NUI_HALIGN_CENTER); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + fHeight = fHeight + 28; + // Add group to the row. + jRow = JsonArrayInsert(JsonArray(), NuiGroup(NuiCol(jGroupCol))); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + string sName = GetName(oPC); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int nToken = SetWindow(oPC, jLayout, "pi_debug_nui", sName + " PEPS Debug Menu", + -1.0, -1.0, 500.0f, fHeight + 12.0f, FALSE, FALSE, TRUE, FALSE, TRUE, "pe_debug"); + // Set all binds, events, and watches. + // Row 1 - Version label. + // Row 2 Module Name. + // Row 3 - 5 Script locations. + // 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_clear_reputation_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_reputation_tooltip", JsonString(" Clears the party's reputation with creature's faction.")); + // Row 7 + NuiSetBind(oPC, nToken, "btn_info_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_info_tooltip", JsonString(" Displays a target object's information to the log screen.")); + NuiSetBind(oPC, nToken, "btn_obj_json_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_obj_json_tooltip", JsonString(" Sends a Json Dump to the log file for the targeted object.")); + NuiSetBind(oPC, nToken, "btn_obj_var_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_obj_var_tooltip", JsonString(" Sends a list of variables for the targeted object.")); + // Row 8 + NuiSetBind(oPC, nToken, "btn_delete_var_tooltip", JsonString(" Delete the variable for the targeted object or Right click for the Module.")); + NuiSetBind(oPC, nToken, "btn_set_var_tooltip", JsonString(" Set the variable for the targeted object or Right click for the Module.")); + NuiSetBind(oPC, nToken, "btn_get_var_tooltip", JsonString(" Get the variable for the targeted object or Right click for the Module.")); + NuiSetBind(oPC, nToken, "cmb_var_type_event", JsonBool(TRUE)); + NuiSetBindWatch(oPC, nToken, "cmb_var_type_selected", TRUE); + // Row 9 + NuiSetBind(oPC, nToken, "txt_var_name_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "txt_var_name", TRUE); + NuiSetBind(oPC, nToken, "txt_var_name_tooltip", JsonString(" Name of the variable we are setting.")); + // Row 10 + NuiSetBind(oPC, nToken, "txt_var_value_event", JsonBool(TRUE)); + NuiSetBindWatch (oPC, nToken, "txt_var_value", TRUE); + NuiSetBind(oPC, nToken, "txt_var_value_tooltip", JsonString(" The value to set on the variable, Objects/Locations will need to be selected.")); + // Row 11 + NuiSetBind(oPC, nToken, "btn_debug_creature_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_debug_creature_tooltip", JsonString(" Sets target creature to send AI debug to the log file.")); + NuiSetBind(oPC, nToken, "btn_clear_events_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_events_tooltip", JsonString(" Sets a creature's event scripts to default.")); + NuiSetBind(oPC, nToken, "btn_clear_debug_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_debug_tooltip", JsonString(" Clears a creature from sending AI debug to the log file.")); +} +int StartingUp(object oPC) +{ + if(GetLocalInt(oPC, AI_ADD_PLUGIN)) + { + json jPlugin = JsonArray(); + jPlugin = JsonArrayInsert(jPlugin, JsonString("pi_debug")); + jPlugin = JsonArrayInsert(jPlugin, JsonInt(FALSE)); + jPlugin = JsonArrayInsert(jPlugin, JsonString("Debug Menu")); + jPlugin = JsonArrayInsert(jPlugin, JsonString("dm_tagsearch")); + json jPlugins = GetLocalJson(oPC, AI_JSON_PLUGINS); + jPlugins = JsonArrayInsert(jPlugins, jPlugin); + SetLocalJson(oPC, AI_JSON_PLUGINS, jPlugin); + SetLocalInt(oPC, AI_PLUGIN_SET, TRUE); + return TRUE; + } + if(!GetLocalInt(oPC, AI_STARTING_UP)) return FALSE; + return TRUE; +} + diff --git a/_module/nss/pi_henchmen.nss b/_module/nss/pi_henchmen.nss new file mode 100644 index 00000000..51190399 --- /dev/null +++ b/_module/nss/pi_henchmen.nss @@ -0,0 +1,209 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pi_henchmen +//////////////////////////////////////////////////////////////////////////////// + Executable plug in script for Philos Module Extentions. + + UI to save a players as Henchmen. +*/////////////////////////////////////////////////////////////////////////////// +#include "pinc_henchmen" +// Does startup check if the game has just been loaded. +int StartingUp(object oPC); +// Inserts base classes to an array for a combo box. +json JArrayInsertBaseClasses(); +void main() +{ + object oPC = OBJECT_SELF; + if(StartingUp(oPC)) return; + // 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")); + // Row 1 (Buttons) ********************************************************* 775 / 73 + json jRow = CreateButtonSelect(JsonArray(), "Party 1", "btn_party1", 90.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButtonSelect(jRow, "Party 2", "btn_party2", 90.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButtonSelect(jRow, "Party 3", "btn_party3", 90.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButtonSelect(jRow, "Party 4", "btn_party4", 90.0f, 20.0f); + jRow = CreateButtonSelect(jRow, "Party 5", "btn_party5", 90.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButtonSelect(jRow, "Party 6", "btn_party6", 90.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButtonSelect(jRow, "Party 7", "btn_party7", 90.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButtonSelect(jRow, "Party 8", "btn_party8", 90.0f, 20.0f); + // Add the row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 (Options)********************************************************** 775 / 101 + jRow = CreateButton(JsonArray(), "Clear Party", "btn_clear_party", 120.0f, 20.0f, -1.0, "btn_clear_party_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Party Join", "btn_join_party", 120.0f, 20.0f, -1.0, "btn_join_party_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButtonSelect(jRow, "Create NPC Henchman", "btn_npc_henchman", 200.0f, 20.0f, "btn_npc_henchman_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Save Party", "btn_save_party", 120.0f, 20.0f, -1.0, "btn_save_party_tooltip"); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + jRow = CreateButton(jRow, "Remove Party", "btn_remove_party", 120.0f, 20.0f, -1.0, "btn_remove_party_tooltip"); + // Add the row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 3 (Names and List titles) ******************************************* 775 / 124 + jRow = CreateLabel(JsonArray(), "", "lbl_save_char", 150.0, 15.0, 0, 0); + jRow = CreateLabel(jRow, "", "lbl_save_list", 200.0, 15.0, 0, 0); + jRow = CreateLabel(jRow, "In game party", "lbl_game_list", 200.0, 15.0, 0, 0); + jRow = CreateLabel(jRow, "", "lbl_game_char", 150.0, 15.0, 0, 0); + // Add the row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Row 4 (List Characters) ************************************************* 775 / 488 (364) + // Saved Characters for Party # + // ***** Adding character saved group next to the button list ************** + json jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jGroupRow = CreateImage(jGroupRow, "", "img_saved_portrait", NUI_ASPECT_EXACTSCALED, NUI_HALIGN_CENTER, NUI_VALIGN_TOP, 128.0, 200.0, 0.0); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + json jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "", "lbl_saved_stats", 150.0, 15.0, 0, 0, 0.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "", "lbl_saved_classes", 150.0, 15.0, 0, 0, 0.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateButton(JsonArray(), "", "btn_saved_join", 75.0, 20.0); + jGroupRow = CreateButton(jGroupRow, "Remove", "btn_saved_remove", 75.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + //jGroupRow = JsonArray(); + //CreateButton(jGroupRow, "Edit", "btn_saved_edit", 150.0, 20.0); + //jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jRow = JsonArrayInsert(JsonArray(), NuiGroup(NuiCol(jGroupCol))); + // Create the button template for the List. + json jButton = NuiId(NuiButton(NuiBind ("btns_saved_char")), "btn_saved_char"); + json jList = JsonArrayInsert(JsonArray (), NuiListTemplateCell(jButton, 170.0, TRUE)); + // Create the list with the template. + jRow = CreateList(jRow, jList, "btns_saved_char", 25.0, 200.0, 325.0); + // Current Characters. + // Create the button template for the List. + jButton = NuiId(NuiButton(NuiBind ("btns_cur_char")), "btn_cur_char"); + jList = JsonArrayInsert(JsonArray (), NuiListTemplateCell(jButton, 170.0, TRUE)); + // Create the list with the template. + jRow = CreateList(jRow, jList, "btns_cur_char", 25.0, 200.0, 325.0); + // ***** Adding character current group next to the button list ************ + jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jGroupRow = CreateImage(jGroupRow, "", "img_cur_portrait", NUI_ASPECT_EXACTSCALED, NUI_HALIGN_CENTER, NUI_VALIGN_TOP, 128.0, 200.0, 0.0); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "", "lbl_cur_stats", 150.0, 15.0, 0, 0, 0.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateLabel(JsonArray(), "", "lbl_cur_classes", 150.0, 15.0, 0, 0, 0.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateButton(JsonArray(), "", "btn_cur_save", 75.0, 20.0); + jGroupRow = CreateButton(jGroupRow, "Remove", "btn_cur_remove", 75.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jGroupRow = CreateButton(JsonArray(), "Edit", "btn_cur_edit", 150.0, 20.0); + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jRow = JsonArrayInsert(jRow, NuiGroup(NuiCol(jGroupCol))); + // Add the row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Set the layout of the window. + json jLayout = NuiCol(jCol); + // Get the window location to restore it from the database. + CheckHenchmanDataAndInitialize(oPC, "0"); + json jData = GetHenchmanDbJson(oPC, "henchman", "0"); + json jGeometry = JsonObjectGet(jData, "henchman_nui"); + float fX = JsonGetFloat(JsonObjectGet(jGeometry, "x")); + float fY = JsonGetFloat(JsonObjectGet(jGeometry, "y")); + if(fX == 0.0 && fY == 0.0) + { + fX = -1.0; + fY = -1.0; + } + string sName = GetName(oPC); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int nToken = SetWindow (oPC, jLayout, "henchman_nui", sName + " party", + fX, fY, 775.0, 488.0, FALSE, FALSE, TRUE, FALSE, TRUE, "pe_henchmen"); + // Lets set MaxHenchman here. + if(GetMaxHenchmen() < 6) SetMaxHenchmen(6); + // Setup watch for saving location. + NuiSetBindWatch (oPC, nToken, "window_geometry", TRUE); + // Set the elements to show events. + NuiSetBind(oPC, nToken, "btn_save_pc_event", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_current_party_event", JsonBool (TRUE)); + string sParty = GetHenchmanDbString(oPC, "henchname", "0"); + if(sParty == "") + { + SetHenchmanDbString(oPC, "henchname", "1", "0"); + sParty = "1"; + } + // Set the party # buttons. + int nIndex; + string sIndex; + for(nIndex = 1; nIndex < 9; nIndex++) + { + sIndex = IntToString(nIndex); + if(sParty == sIndex) NuiSetBind(oPC, nToken, "btn_party" + sIndex, JsonBool(TRUE)); + else NuiSetBind(oPC, nToken, "btn_party" + sIndex, JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_party" + sIndex + "_event", JsonBool (TRUE)); + } + NuiSetBind(oPC, nToken, "btn_npc_henchman_event", JsonBool(TRUE)); + string sText = " Select a creature to copy and have them join you."; + NuiSetBind(oPC, nToken, "btn_npc_henchman_tooltip", JsonString(sText)); + // ********** Saved Henchman in party # ********* + nIndex = 0; + int nSlot, nMaxHenchman = AI_MAX_HENCHMAN + 1; + json jButtons = JsonArray(); + string sFirstHenchman, sButtonText; + json jNPCs, jNPC; + // Add saved party members from sParty to the button list. + while(nIndex < nMaxHenchman) + { + sIndex = IntToString(nIndex); + sButtonText = GetHenchmanDbString(oPC, "henchname", sParty + sIndex); + if(sButtonText != "") + { + jButtons = JsonArrayInsert(jButtons, JsonString(sButtonText)); + SetHenchmanDbString(oPC, "slot", sParty + IntToString(nSlot++), sParty + sIndex); + } + nIndex++; + } + // Add the buttons to the list. + NuiSetBind(oPC, nToken, "btns_saved_char", jButtons); + // Set up button lables for henchman. + NuiSetBind(oPC, nToken, "lbl_save_list_label", JsonString("Party Save " + sParty)); + AddSavedCharacterInfo(oPC, nToken, sParty); + // ********** Current Party ********* + NuiSetBind(oPC, nToken, "btn_current_party", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_clear_party", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "lbl_save_char", JsonBool(TRUE)); + // Set up button labels for henchman. + NuiSetBind(oPC, nToken, "btn_join_save_label", JsonString("Save")); + nIndex = 0; + jButtons = JsonArray(); + object oPartyMember, oCharacter = OBJECT_INVALID; + // Add current party members to the button list. + while(nIndex < AI_MAX_HENCHMAN) + { + if(nIndex == 0) oPartyMember = oPC; + else oPartyMember = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oPartyMember != OBJECT_INVALID) jButtons = JsonArrayInsert(jButtons, JsonString(GetName(oPartyMember))); + else break; + nIndex++; + } + // Add the buttons to the list. + NuiSetBind(oPC, nToken, "btns_cur_char", jButtons); + AddCurrentCharacterInfo(oPC, nToken, sParty); +} +int StartingUp(object oPC) +{ + if(GetLocalInt(oPC, AI_ADD_PLUGIN)) + { + json jPlugin = JsonArray(); + jPlugin = JsonArrayInsert(jPlugin, JsonString("pi_henchmen")); + jPlugin = JsonArrayInsert(jPlugin, JsonInt(FALSE)); + jPlugin = JsonArrayInsert(jPlugin, JsonString("Henchmen Menu")); + jPlugin = JsonArrayInsert(jPlugin, JsonString("dm_creator")); + json jPlugins = GetLocalJson(oPC, AI_JSON_PLUGINS); + jPlugins = JsonArrayInsert(jPlugins, jPlugin); + SetLocalJson(oPC, AI_JSON_PLUGINS, jPlugin); + SetLocalInt(oPC, AI_PLUGIN_SET, TRUE); + return TRUE; + } + if(!GetLocalInt(oPC, AI_STARTING_UP)) return FALSE; + return TRUE; +} + diff --git a/_module/nss/pinc_henchmen.nss b/_module/nss/pinc_henchmen.nss new file mode 100644 index 00000000..a65adf4b --- /dev/null +++ b/_module/nss/pinc_henchmen.nss @@ -0,0 +1,1541 @@ +/*////////////////////////////////////////////////////////////////////////////// +// Script Name: pinc_henchmen +//////////////////////////////////////////////////////////////////////////////// + Include file for Henchmen plug in scripts for Philos Module Extentions. + +Database Info: +Slot 0 - henchname = the save slot 1 - 8. +Slots 1 - 8 define the selections: + henchname = Saved character selected. + image = Current character selected. +*/////////////////////////////////////////////////////////////////////////////// +#include "0i_nui" +#include "nw_inc_gff" + +const string HENCHMAN_DATABASE = "philos_henchman_db"; +const string HENCHMAN_TABLE = "HENCHMAN_TABLE"; +const string HENCHMAN_TO_EDIT = "HENCHMAN_TO_EDIT"; + +// Creates the table and initializes if it needs to. +void CheckHenchmanDataAndInitialize(object oPC, string sSlot); +// Removes a henchan from the current slot. +void RemoveHenchmanDb(object oPC, string sSlot); +// sDataField should be one of the data fields for that table. +// sData is the string data to be saved. +void SetHenchmanDbString(object oPC, string sDataField, string sData, string sSlot); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +string GetHenchmanDbString(object oPC, string sDataField, string sSlot); +// sDataField should be one of the data fields for that table. +// jData is the json data to be saved. +void SetHenchmanDbJson(object oPC, string sDataField, json jData, string sSlot); +// sDataField should be one of the data fields for the table. +// Returns a string of the data stored. +json GetHenchmanDbJson(object oPC, string sDataField, string sSlot); +// sSlot is the slot to define this object in the database for this Slot## (# Party button and #1-6). +// oHenchman is the PC/Henchman to be saved. +void SetHenchmanDbObject(object oPC, object oHenchman, string sSlot); +// sSlot is the slot to define this object in the database for this Slot## (# Party button and #1-6). +// lLocationToSpawn will spawn the object at that location. +object GetHenchmanDbObject(object oPC, location lLocationToSpawn, string sSlot); +// Returns TRUE if the henchman with sName can join. +int GetJoinButtonActive(object oPC, string sName); +// Returns a two letter alignment string. +string GetAlignText(object oHenchman); +// Populates the Saved character group. +void AddSavedCharacterInfo(object oPC, int nToken, string sParty); +// Populates the Current character group. +void AddCurrentCharacterInfo(object oPC, int nToken, string sParty); +// Removes a henchman from your party. +void RemoveYourHenchman(object oPC, int nToken, string sParty); +// Removes all henchman from the party. +void RemoveWholeParty(object oPC, int nToken, string sParty); +// Saves a henchman in your party to the saved party #. +void SaveYourHenchman(object oPC, int nToken, string sParty); +// Saves the whole party to the saved party #. +void SaveWholeParty(object oPC, int nToken, string sParty); +// Saves the players current party to party #. +void SavedPartyJoin(object oPC, int nToken, string sParty); +// Saves a character in the players party to party #. +void SavedCharacterJoin(object oPC, int nToken, string sParty); +// Clears the players saved party #. +void SavedPartyCleared(object oPC, int nToken, string sParty); +// Sets oHenchmans scripts to the current AI. +void SetHenchmanScripts(object oHenchman); +// If a henchman does not have a LvlStatList this will create one for them. +// nLevels allows the creation of x levels for LvlStatList using the 1st class. +// 0 on nLevels makes the function build it based on current levels. +json CreateLevelStatList(json jHenchman, object oHenchman, object oPC, int nLevels = 0); +// Resets the character to level one in the first class. +object ResetCharacter(object oPC, object oHenchman); +// Creates a menu to edit a characters information. +void CreateCharacterEditGUIPanel(object oPC, object oAssociate); +// Creates a character description menu. +void CreateCharacterDescriptionNUI(object oPC, string sName, string sIcon, string sDescription); + +void CreateHenchmanDataTable () +{ + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, + "CREATE TABLE IF NOT EXISTS " + HENCHMAN_TABLE + " (" + + "name TEXT, " + + "slot TEXT, " + + "henchname TEXT, " + + "image TEXT, " + + "stats TEXT, " + + "classes TEXT, " + + "henchman TEXT, " + + "PRIMARY KEY(slot));"); + SqlStep (sql); +} +void CheckHenchmanDataAndInitialize(object oPC, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "SELECT name FROM sqlite_master WHERE type ='table' AND name=@tableName;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindString(sql, "@tableName", HENCHMAN_TABLE); + if(!SqlStep (sql)) CreateHenchmanDataTable(); + sQuery = "SELECT slot FROM " + HENCHMAN_TABLE + " Where name = @name AND slot = @slot;"; + sql = SqlPrepareQueryCampaign("philos_henchman_db", sQuery); + SqlBindString(sql, "@name", sPCName); + SqlBindString(sql, "@slot", sSlot); + if(!SqlStep(sql)) + { + sQuery = "INSERT INTO " + HENCHMAN_TABLE + "(name, slot, henchname, image, stats, classes " + + ", henchman) VALUES (@name, @slot, @henchname, @image, @stats, @classes, @henchman);"; + sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindString(sql, "@name", sPCName); + SqlBindString(sql, "@slot", sSlot); + SqlBindString(sql, "@henchname", ""); + SqlBindString(sql, "@image", ""); + SqlBindString(sql, "@stats", ""); + SqlBindString(sql, "@classes", ""); + SqlBindJson(sql, "@henchman", JsonObject()); + SqlStep(sql); + } +} +void RemoveHenchmanDb(object oPC, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "DELETE FROM " + HENCHMAN_TABLE + " WHERE " + + "name = @name AND slot = @slot;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindString(sql, "@name", sPCName); + SqlBindString(sql, "@slot", sSlot); + SqlStep(sql); +} +void SetHenchmanDbString(object oPC, string sDataField, string sData, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "UPDATE " + HENCHMAN_TABLE + " SET " + sDataField + " = @data WHERE " + + "name = @name AND slot = @slot;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindString(sql, "@data", sData); + SqlBindString(sql, "@name", sPCName); + SqlBindString(sql, "@slot", sSlot); + SqlStep(sql); +} +string GetHenchmanDbString(object oPC, string sDataField, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "SELECT " + sDataField + " FROM " + HENCHMAN_TABLE + " WHERE " + + "name = @name AND slot = @slot;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindString(sql, "@name", sPCName); + SqlBindString(sql, "@slot", sSlot); + if(SqlStep (sql)) return SqlGetString(sql, 0); + else return ""; +} +void SetHenchmanDbJson(object oPC, string sDataField, json jData, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "UPDATE " + HENCHMAN_TABLE + " SET " + sDataField + + " = @data WHERE name = @name AND slot = @slot;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindJson (sql, "@data", jData); + SqlBindString(sql, "@name", sPCName); + SqlBindString (sql, "@slot", sSlot); + SqlStep (sql); +} +json GetHenchmanDbJson(object oPC, string sDataField, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "SELECT " + sDataField + " FROM " + HENCHMAN_TABLE + " WHERE " + + "name = @name AND slot = @slot;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindString(sql, "@name", sPCName); + SqlBindString (sql, "@slot", sSlot); + if (SqlStep (sql)) return SqlGetJson (sql, 0); + else return JsonArray (); +} +void SetHenchmanDbObject(object oPC, object oHenchman, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "UPDATE " + HENCHMAN_TABLE + " SET henchman = @henchman WHERE " + + "name = @name AND slot = @slot;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindObject(sql, "@henchman", oHenchman); + SqlBindString(sql, "@name", sPCName); + SqlBindString(sql, "@slot", sSlot); + SqlStep(sql); +} +object GetHenchmanDbObject(object oPC, location lLocationToSpawn, string sSlot) +{ + string sPCName = ai_RemoveIllegalCharacters(GetPCPlayerName(oPC)); + string sQuery = "SELECT henchman FROM " + HENCHMAN_TABLE + " WHERE " + + "name = @name AND slot = @slot;"; + sqlquery sql = SqlPrepareQueryCampaign(HENCHMAN_DATABASE, sQuery); + SqlBindString(sql, "@name", sPCName); + SqlBindString (sql, "@slot", sSlot); + if (SqlStep (sql)) + { + json jHenchman = SqlGetJson(sql, 0); + string sTag = JsonGetString(GffGetString(jHenchman, "Tag")); + if(sTag == "") jHenchman = GffReplaceString(jHenchman, "Tag", "Hench_" + IntToString(Random(100))); + return JsonToObject(jHenchman, lLocationToSpawn, OBJECT_INVALID, TRUE); + } + return OBJECT_INVALID; +} +int GetJoinButtonActive(object oPC, string sName) +{ + if(sName == GetName(oPC)) return FALSE; + // Look for a free henchman slot, and if this henchman is already joined! + int nIndex = 1; + object oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + while(oHenchman != OBJECT_INVALID) + { + if(GetName(oHenchman) == sName) return FALSE; + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, ++nIndex); + } + return TRUE; +} +string GetAlignText(object oHenchman) +{ + string sAlign1, sAlign2; + switch (GetAlignmentLawChaos(oHenchman)) + { + case ALIGNMENT_LAWFUL : sAlign1 = "L"; break; + case ALIGNMENT_NEUTRAL : sAlign1 = "N"; break; + case ALIGNMENT_CHAOTIC : sAlign1 = "C"; break; + } + switch (GetAlignmentGoodEvil(oHenchman)) + { + case ALIGNMENT_GOOD : sAlign2 = "G"; break; + case ALIGNMENT_NEUTRAL : sAlign2 = "N"; break; + case ALIGNMENT_EVIL : sAlign2 = "E"; break; + } + string sAlign = sAlign1 + sAlign2; + if (sAlign == "NN") sAlign = "TN"; + return sAlign; +} +void AddSavedCharacterInfo(object oPC, int nToken, string sParty) +{ + string sHenchman = GetHenchmanDbString(oPC, "henchname", sParty); + // Add Henchman information. + if(sHenchman != "") + { + NuiSetBind (oPC, nToken, "btn_clear_party_event", JsonBool (TRUE)); + string sText = " Clears all characters from party " + sParty + "'s list!"; + NuiSetBind(oPC, nToken, "btn_clear_party_tooltip", JsonString(sText)); + NuiSetBind(oPC, nToken, "btn_join_party", JsonBool (TRUE)); + NuiSetBind(oPC, nToken, "btn_join_party_event", JsonBool (TRUE)); + sText = " Saved characters from party " + sParty + " enter the game and join you."; + NuiSetBind(oPC, nToken, "btn_join_party_tooltip", JsonString(sText)); + // Setup the henchman window. + string sName = GetHenchmanDbString(oPC, "henchname", sParty + sHenchman); + string sImage = GetHenchmanDbString(oPC, "image", sParty + sHenchman); + string sStats = GetHenchmanDbString(oPC, "stats", sParty + sHenchman); + string sClasses = GetHenchmanDbString(oPC, "classes", sParty + sHenchman); + NuiSetBind(oPC, nToken, "lbl_save_char_label", JsonString(sName)); + NuiSetBind(oPC, nToken, "img_saved_portrait_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "img_saved_portrait_image", JsonString(sImage + "l")); + NuiSetBind(oPC, nToken, "lbl_saved_stats_label", JsonString(sStats)); + NuiSetBind(oPC, nToken, "lbl_saved_classes_label", JsonString(sClasses)); + NuiSetBind(oPC, nToken, "btn_saved_join_label", JsonString("Join")); + NuiSetBind(oPC, nToken, "btn_saved_join_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_saved_remove_event", JsonBool(TRUE)); + //NuiSetBind(oPC, nToken, "btn_saved_edit_event", JsonBool(TRUE)); + } + else + { + NuiSetBind(oPC, nToken, "lbl_save_char_label", JsonString("Empty Party")); + NuiSetBind (oPC, nToken, "btn_clear_party_event", JsonBool (FALSE)); + NuiSetBind (oPC, nToken, "btn_join_party", JsonBool (FALSE)); + NuiSetBind (oPC, nToken, "btn_join_party_event", JsonBool (FALSE)); + // Setup the henchman window. + NuiSetBind(oPC, nToken, "img_saved_portrait_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "img_saved_portrait_image", JsonString("po_hu_m_99_l")); + NuiSetBind(oPC, nToken, "lbl_saved_stats_label", JsonString("")); + NuiSetBind(oPC, nToken, "lbl_saved_classes_label", JsonString("")); + NuiSetBind(oPC, nToken, "btn_saved_join_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_saved_join_label", JsonString("Join")); + NuiSetBind(oPC, nToken, "btn_saved_remove_event", JsonBool(FALSE)); + NuiSetBind(oPC, nToken, "btn_saved_edit_event", JsonBool(FALSE)); + } +} +void AddCurrentCharacterInfo(object oPC, int nToken, string sParty) +{ + string sHenchman = GetHenchmanDbString(oPC, "image", sParty); + if(sHenchman == "") + { + CheckHenchmanDataAndInitialize(oPC, sParty); + SetHenchmanDbString(oPC, "image", "0", sParty); + } + int nHenchman = StringToInt(sHenchman); + int nIndex = 0; + object oCharacter; + while(nIndex < AI_MAX_HENCHMAN) + { + if(nIndex == 0) oCharacter = oPC; + else oCharacter = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oCharacter == OBJECT_INVALID) + { + nIndex = 0; + oCharacter = oPC; + break; + } + else if(nHenchman == nIndex) break; + nIndex++; + } + // Adjust the party buttons. + int bParty = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, 1) != OBJECT_INVALID; + //NuiSetBind(oPC, nToken, "btn_save_party", JsonBool (bParty)); + NuiSetBind(oPC, nToken, "btn_save_party_event", JsonBool (bParty)); + //NuiSetBind(oPC, nToken, "btn_remove_party", JsonBool (bParty)); + NuiSetBind(oPC, nToken, "btn_remove_party_event", JsonBool (bParty)); + if(bParty) + { + string sText = " Saves all henchman from your current party to party " + sParty + "."; + NuiSetBind(oPC, nToken, "btn_save_party_tooltip", JsonString(sText)); + sText = " Removes all henchman from your current party!"; + NuiSetBind(oPC, nToken, "btn_remove_party_tooltip", JsonString(sText)); + } + // Setup the henchman window. + string sName = GetName(oCharacter); + string sImage = GetPortraitResRef(oCharacter); + string sStats = GetAlignText(oCharacter) + " "; + if(GetGender(oCharacter) == GENDER_MALE) sStats += "Male "; + else sStats += "Female "; + int nPosition = 1; + sStats += GetStringByStrRef (StringToInt (Get2DAString ("racialtypes", "Name", GetRacialType (oCharacter)))); + string sClasses = GetStringByStrRef (StringToInt (Get2DAString ("classes", "Short", GetClassByPosition (nPosition, oCharacter)))); + sClasses += " " + IntToString (GetLevelByPosition (nPosition, oCharacter)); + int nClass = GetClassByPosition(++nPosition, oCharacter); + while(nClass != CLASS_TYPE_INVALID) + { + sClasses += ", " + GetStringByStrRef (StringToInt (Get2DAString ("classes", "Short", nClass))); + sClasses += " " + IntToString (GetLevelByPosition (nPosition, oCharacter)); + nClass = GetClassByPosition(++nPosition, oCharacter); + } + NuiSetBind(oPC, nToken, "lbl_game_char_label", JsonString(sName)); + NuiSetBind(oPC, nToken, "img_cur_portrait_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "img_cur_portrait_image", JsonString(sImage + "l")); + NuiSetBind(oPC, nToken, "lbl_cur_stats_label", JsonString(sStats)); + NuiSetBind(oPC, nToken, "lbl_cur_classes_label", JsonString(sClasses)); + NuiSetBind(oPC, nToken, "btn_cur_save_label", JsonString("Save")); + NuiSetBind(oPC, nToken, "btn_cur_save_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cur_edit_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_cur_remove_event", JsonBool(TRUE)); +} +object GetSelectedHenchman(object oPC, string sParty) +{ + string sHenchman = GetHenchmanDbString(oPC, "image", sParty); + if(sHenchman == "") + { + CheckHenchmanDataAndInitialize(oPC, sParty); + SetHenchmanDbString(oPC, "image", "0", sParty); + } + int nHenchman = StringToInt(sHenchman); + int nIndex = 0; + object oCharacter; + while(nIndex < AI_MAX_HENCHMAN) + { + if(nIndex == 0) oCharacter = oPC; + else oCharacter = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oCharacter == OBJECT_INVALID) + { + nIndex = 0; + oCharacter = oPC; + break; + } + else if(nHenchman == nIndex) break; + nIndex++; + } + return oCharacter; +} +void RemoveYourHenchman(object oPC, int nToken, string sParty) +{ + object oHenchman = GetSelectedHenchman(oPC, sParty); + if(oHenchman == oPC) ai_SendMessages("You cannot remove the player from the party!", AI_COLOR_RED, oPC); + else + { + RemoveHenchman(oPC, oHenchman); + 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); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_henchmen", oPC); +} +void RemoveWholeParty(object oPC, int nToken, string sParty) +{ + int nIndex = AI_MAX_HENCHMAN; + object oHenchman; + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + while(nIndex > 0) + { + if(oHenchman != OBJECT_INVALID) + { + ai_SendMessages(GetName(oHenchman) + " has been remove from your Party.", AI_COLOR_YELLOW, oPC); + RemoveHenchman(oPC, oHenchman); + AssignCommand(oHenchman, SetIsDestroyable(TRUE, FALSE, FALSE)); + NuiDestroy(oPC, NuiFindWindow(oPC, ai_GetAssociateType(oPC, oHenchman) + AI_WIDGET_NUI)); + DestroyObject(oHenchman); + } + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, --nIndex); + } + ai_SendMessages("All of your henchman have been remove from the Party.", AI_COLOR_YELLOW, oPC); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_henchmen", oPC); +} +void SaveYourHenchman(object oPC, int nToken, string sParty) +{ + int bPC, nIndex, nClass, nPosition, nMaxHenchman = AI_MAX_HENCHMAN + 1; + string sName, sIndex, sSlot, sStats, sClasses; + object oHenchman = GetSelectedHenchman(oPC, sParty); + if(oHenchman == oPC) + { + bPC = TRUE; + oHenchman = CopyObject(oPC, GetLocation(oPC), OBJECT_INVALID, "hench_" + IntToString(Random(100)), TRUE); + SetHenchmanScripts(oHenchman); + } + string sHenchmanName = GetName(oHenchman); + while(nIndex < nMaxHenchman) + { + sIndex = IntToString(nIndex); + sName = GetHenchmanDbString(oPC, "henchname", sParty + sIndex); + if(sName == sHenchmanName || sName == "") + { + sSlot = sParty + sIndex; + if(!bPC) RemoveHenchman(oPC, oHenchman); + // Special check for Infinite Dungeon plot givers to be changed into henchman. + if(GetStringLeft(GetLocalString(oHenchman, "sConversation"), 8) == "id1_plot") + { + DeleteLocalString(oHenchman, "sConversation"); + } + 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); + CheckHenchmanDataAndInitialize(oPC, sSlot); + SetHenchmanDbString(oPC, "image", GetPortraitResRef(oHenchman), sSlot); + SetHenchmanDbString(oPC, "henchname", sHenchmanName, sSlot); + sStats = GetAlignText(oHenchman) + " "; + if(GetGender(oHenchman) == GENDER_MALE) sStats += "Male "; + else sStats += "Female "; + nPosition = 1; + sStats += GetStringByStrRef (StringToInt (Get2DAString ("racialtypes", "Name", GetRacialType (oHenchman)))); + sClasses = GetStringByStrRef (StringToInt (Get2DAString ("classes", "Short", GetClassByPosition (nPosition, oHenchman)))); + sClasses += " " + IntToString (GetLevelByPosition (nPosition, oHenchman)); + nClass = GetClassByPosition(++nPosition, oHenchman); + while(nClass != CLASS_TYPE_INVALID) + { + sClasses += ", " + GetStringByStrRef (StringToInt (Get2DAString ("classes", "Short", GetClassByPosition (nPosition, oHenchman)))); + sClasses += " " + IntToString (GetLevelByPosition (nPosition, oHenchman)); + nClass = GetClassByPosition(++nPosition, oHenchman); + } + 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); + break; + } + nIndex++; + } +if(nIndex == nMaxHenchman) ai_SendMessages("This party is full!", AI_COLOR_RED, oPC); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_henchmen", oPC); +} +void SaveWholeParty(object oPC, int nToken, string sParty) +{ + int nIndex = AI_MAX_HENCHMAN; + object oHenchman; + while(nIndex > 0) + { + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + if(oHenchman != OBJECT_INVALID) + { + SetHenchmanDbString(oPC, "image", IntToString(nIndex), sParty); + SaveYourHenchman(oPC, nToken, sParty); + } + nIndex--; + } + ai_SendMessages("All of your henchman have been saved to Party " + sParty + ".", AI_COLOR_YELLOW, oPC); + SetHenchmanDbString(oPC, "henchname", "0", sParty); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_henchmen", oPC); +} +void SavedPartyJoin(object oPC, int nToken, string sParty) +{ + int bFound, nIndex, nDBHenchman = 0; + json jHenchman; + object oHenchman, oLoadedHenchman; + string sDBHenchman = IntToString(nDBHenchman); + string sName = GetHenchmanDbString(oPC, "henchname", sParty + sDBHenchman); + while(sName != "") + { + bFound = FALSE; + nIndex = 1; + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + while(oHenchman != OBJECT_INVALID) + { + if(sName == GetName(oPC) || GetName(oHenchman) == sName) + { + bFound = TRUE; + break; + } + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, ++nIndex); + } + if(!bFound) + { + ai_SendMessages(sName + " has joined your party.", AI_COLOR_GREEN, oPC); + jHenchman = GetHenchmanDbJson(oPC, "henchman", sParty + sDBHenchman); + oLoadedHenchman = JsonToObject(jHenchman, GetLocation(oPC), OBJECT_INVALID, TRUE); + AddHenchman(oPC, oLoadedHenchman); + } + else ai_SendMessages(sName + " is already in your party!", AI_COLOR_RED, oPC); + sDBHenchman = IntToString(++nDBHenchman); + sName = GetHenchmanDbString(oPC, "henchname", sParty + sDBHenchman); + } + NuiDestroy(oPC, nToken); + ExecuteScript("pi_henchmen", oPC); +} +void SavedCharacterJoin(object oPC, int nToken, string sParty) +{ + int nIndex, bFound; + object oHenchman, oLoadedHenchman; + string sHenchman = GetHenchmanDbString(oPC, "henchname", sParty); + string sName = GetHenchmanDbString(oPC, "henchname", sParty + sHenchman); + nIndex = 1; + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex); + while(oHenchman != OBJECT_INVALID) + { + if(sName == GetName(oPC) || GetName(oHenchman) == sName) + { + bFound = TRUE; + break; + } + oHenchman = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, ++nIndex); + } + if(!bFound) + { + ai_SendMessages(sName + " has joined your party!", AI_COLOR_GREEN, oPC); + oLoadedHenchman = GetHenchmanDbObject(oPC, GetLocation(oPC), sParty + sHenchman); + AddHenchman(oPC, oLoadedHenchman); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_henchmen", oPC); + } + else ai_SendMessages(sName + " is already in your party!", AI_COLOR_RED, oPC); +} +void SavedPartyCleared(object oPC, int nToken, string sParty) +{ + int nIndex, nMaxHenchman = AI_MAX_HENCHMAN + 1; + object oHenchman, oLoadedHenchman; + string sIndex = IntToString(nIndex); + string sName = GetHenchmanDbString(oPC, "henchname", sParty + sIndex); + while(nIndex < nMaxHenchman) + { + if(sName != "") + { + RemoveHenchmanDb(oPC, sParty + sIndex); + ai_SendMessages(sName + " has been cleared from the saved party " + sParty + ".", AI_COLOR_YELLOW, oPC); + } + sIndex = IntToString(++nIndex); + sName = GetHenchmanDbString(oPC, "henchname", sParty + sIndex); + } + SetHenchmanDbString(oPC, "henchname", "", sParty); + NuiDestroy(oPC, nToken); + ExecuteScript("pi_henchmen", oPC); +} +json CreateOptionsAlignment(object oHenchman, int nAlignType) +{ + json jAlignNameList = JsonArray(); + if(nAlignType == 0) + { + jAlignNameList = JsonArrayInsert(jAlignNameList, JsonString("Lawful")); + jAlignNameList = JsonArrayInsert(jAlignNameList, JsonString("Neutral")); + jAlignNameList = JsonArrayInsert(jAlignNameList, JsonString("Chaotic")); + } + else + { + jAlignNameList = JsonArrayInsert(jAlignNameList, JsonString("Good")); + jAlignNameList = JsonArrayInsert(jAlignNameList, JsonString("Neutral")); + jAlignNameList = JsonArrayInsert(jAlignNameList, JsonString("Evil")); + } + return jAlignNameList; +} +json CreateOptionsClasses(object oHenchman) +{ + int nIndex = 1, nClass; + string sClassName; + json jClassNameList = JsonArray(); + while(nIndex < 5) + { + nClass = GetClassByPosition(nIndex, oHenchman); + if(nClass == CLASS_TYPE_INVALID) sClassName = "Empty"; + else + { + sClassName = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nClass))); + sClassName += " " + IntToString(GetLevelByClass(nClass, oHenchman)); + } + jClassNameList = JsonArrayInsert(jClassNameList, JsonString(sClassName)); + nIndex++; + } + return jClassNameList; +} +json jArrayInsertClasses() +{ + int nIndex, nClass, nMaxClass = Get2DARowCount("classes"); + string sClassName; + json jClassNameCombo = JsonArray(); + while(nIndex < nMaxClass) + { + if(Get2DAString("classes", "PlayerClass", nIndex) == "1") + { + sClassName = GetStringByStrRef(StringToInt(Get2DAString("classes", "Name", nIndex))); + jClassNameCombo = JsonArrayInsert(jClassNameCombo, NuiComboEntry(sClassName, nClass)); + nClass++; + } + nIndex++; + } + return jClassNameCombo; +} +int GetSelectionByClass2DA(int nClass) +{ + int nIndex, nSelection, nMaxClass = Get2DARowCount("classes"); + while(nIndex < nMaxClass) + { + if(Get2DAString("classes", "PlayerClass", nIndex) == "1") + { + if(nClass == nIndex) return nSelection; + nSelection++; + } + nIndex++; + } + return -1; +} +int GetClassBySelection2DA(int nSelection) +{ + int nIndex, nClass, nMaxClass = Get2DARowCount("classes"); + while(nClass < nMaxClass) + { + if(Get2DAString("classes", "PlayerClass", nClass) == "1") + { + if(nSelection == nIndex) return nClass; + nIndex++; + } + nClass++; + } + return -1; +} +json ArrayInsertPackages(string sClass) +{ + int nIndex, nPackage, nMaxPackage = Get2DARowCount("packages"); + string sPackageName; + json jPackageNameCombo = JsonArray(); + while(nIndex < nMaxPackage) + { + if(Get2DAString("packages", "ClassID", nIndex) == sClass) + { + sPackageName = Get2DAString("packages", "Label", nIndex); + //GetStringByStrRef(StringToInt(Get2DAString("packages", "Name", nIndex))); + if(sPackageName != "Bad Strref" && sPackageName != "") + { + jPackageNameCombo = JsonArrayInsert(jPackageNameCombo, NuiComboEntry(sPackageName, nPackage)); + nPackage++; + } + } + nIndex++; + } + return jPackageNameCombo; +} +int GetSelectionByPackage2DA(string sClass, int nPackage) +{ + int nIndex, nSelection, nMaxPackage = Get2DARowCount("packages"); + string sPackageName; + while(nIndex < nMaxPackage) + { + if(Get2DAString("packages", "ClassID", nIndex) == sClass) + { + sPackageName = GetStringByStrRef(StringToInt(Get2DAString("packages", "Name", nIndex))); + if(sPackageName != "Bad Strref" && sPackageName != "") + { + if(nPackage == nIndex) return nSelection; + nSelection++; + } + } + nIndex++; + } + return -1; +} +int GetPackageBySelection2DA(string sClass, int nSelection) +{ + int nIndex, nPackage, nMaxPackage = Get2DARowCount("packages"); + while(nPackage < nMaxPackage) + { + if(Get2DAString("packages", "ClassID", nPackage) == sClass) + { + if(nSelection == nIndex) return nPackage; + nIndex++; + } + nPackage++; + } + return -1; +} +json ArrayInsertSoundSets(object oHenchman) +{ + int nIndex, nSoundSet, nSoundSetType, nMaxSets = Get2DARowCount("soundset"); + string sGender = IntToString(GetGender(oHenchman)); + string sSoundSetName, sResRef; + json jSoundSetNameCombo = JsonArray(); + while(nIndex < nMaxSets) + { + if(Get2DAString("soundset", "GENDER", nIndex) == sGender) + { + nSoundSetType = StringToInt(Get2DAString("soundset", "TYPE", nIndex)); + if(nSoundSetType < 5) + { + sSoundSetName = GetStringByStrRef(StringToInt(Get2DAString("soundset", "STRREF", nIndex))); + sResRef = GetStringLowerCase(Get2DAString("soundset", "RESREF", nIndex)); + if(GetStringLeft(sResRef, 4) == "vs_f") sSoundSetName += " (Full)"; + else if(GetStringLeft(sResRef, 4) == "vs_n") sSoundSetName += " (Part)"; + jSoundSetNameCombo = JsonArrayInsert(jSoundSetNameCombo, NuiComboEntry(sSoundSetName, nSoundSet)); + nSoundSet++; + } + } + nIndex++; + } + return jSoundSetNameCombo; +} +int GetSelectionBySoundSet2DA(object oHenchman, int nSoundSet) +{ + int nIndex, nSelection, nSoundSetType, nMaxSoundSet = Get2DARowCount("soundset"); + string sGender = IntToString(GetGender(oHenchman)); + while(nIndex < nMaxSoundSet) + { + if(Get2DAString("soundset", "GENDER", nIndex) == sGender) + { + nSoundSetType = StringToInt(Get2DAString("soundset", "TYPE", nIndex)); + if(nSoundSetType < 5) + { + if(nSoundSet == nIndex) return nSelection; + nSelection++; + } + } + nIndex++; + } + return -1; +} +int GetSoundSetBySelection2DA(object oHenchman, int nSelection) +{ + int nIndex, nSoundSet, nSoundSetType, nMaxSoundSet = Get2DARowCount("soundset"); + string sGender = IntToString(GetGender(oHenchman)); + while(nSoundSet < nMaxSoundSet) + { + if(Get2DAString("soundset", "GENDER", nSoundSet) == sGender) + { + nSoundSetType = StringToInt(Get2DAString("soundset", "TYPE", nSoundSet)); + if(nSoundSetType < 5) + { + if(nSelection == nIndex) return nSoundSet; + nIndex++; + } + } + nSoundSet++; + } + return -1; +} +void SetHenchmanScripts(object oHenchman) +{ + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, "nw_ch_ac1"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_NOTICE, "nw_ch_ac2"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, "nw_ch_ac3"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, "nw_ch_ac4"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, "nw_ch_ac5"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_DAMAGED, "nw_ch_ac6"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_DEATH, "nw_ch_ac7"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_DISTURBED, "nw_ch_ac8"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_SPAWN_IN, "nw_ch_ac9"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_RESTED, "nw_ch_aca"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, "nw_ch_acb"); + SetEventScript(oHenchman, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, "nw_ch_ace"); +} +object ai_AddHenchman(object oPC, json jHenchman, location lLocation, int nFamiliar, int nCompanion) +{ + jHenchman = GffReplaceResRef(jHenchman, "ScriptSpawn", ""); + object oHenchman = JsonToObject(jHenchman, lLocation, OBJECT_INVALID, TRUE); + AddHenchman(oPC, oHenchman); + DeleteLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE"); + string sAssociateType = ai_GetAssociateType(oPC, oHenchman); + NuiDestroy(oPC, NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI)); + if(nFamiliar) SummonFamiliar(oHenchman); + if(nCompanion) SummonAnimalCompanion(oHenchman); + return oHenchman; +} +json CreateLevelStatList(json jHenchman, object oHenchman, object oPC, int nLevels = 0) +{ + int nClass = GetClassByPosition(1, oHenchman); + int nHitDie = StringToInt(Get2DAString("classes", "HitDie", nClass)); + SetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE", TRUE); + json jSkill = JsonObject(); + jSkill = GffAddByte(jSkill, "Rank", 0); + jSkill = JsonObjectSet(jSkill, "__struct_id", JsonInt(0)); + json jSkillArray = JsonArray(); + int nNumOfSkills; + for(nNumOfSkills = Get2DARowCount("skills"); nNumOfSkills > 0; nNumOfSkills--) + { + jSkillArray = JsonArrayInsert(jSkillArray, jSkill); + } + json jLevel = JsonObject(); + jLevel = GffAddByte(jLevel, "EpicLevel", 0); + jLevel = GffAddList(jLevel, "FeatList", JsonArray()); + jLevel = GffAddByte(jLevel, "LvlStatClass", nClass); + jLevel = GffAddByte(jLevel, "LvlStatHitDie", nHitDie); + jLevel = GffAddList(jLevel, "SkillList", jSkillArray); + jLevel = GffAddWord(jLevel, "SkillPoints", 0); + jLevel = JsonObjectSet(jLevel, "__struct_id", JsonInt(0)); + json jLevelArray = JsonArray(); + if(nLevels == 0) nLevels = GetLevelByPosition(1, oHenchman); + for(nLevels; nLevels > 0; nLevels--) + { + jLevelArray = JsonArrayInsert(jLevelArray, jLevel); + } + 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) +{ + // Check if all classes can use. + int n2DAStat = StringToInt(Get2DAString("feat", "ALLCLASSESCANUSE", nFeat)); + if(n2DAStat == 0) + { + int bPass, nClassFeat, nRow, nClass = GetClassByPosition(nPosition, oCreature); + string sClsFeat2DAName = Get2DAString("classes", "FeatsTable", nClass); + int nMaxRow = Get2DARowCount(sClsFeat2DAName); + while(nRow < nMaxRow) + { + nClassFeat = StringToInt(Get2DAString(sClsFeat2DAName, "FeatIndex", nRow)); + if(nClassFeat == nFeat) + { + bPass = TRUE; + break; + } + nRow++; + } + if(!bPass) return FALSE; + } + n2DAStat = StringToInt(Get2DAString("feat", "MINATTACKBONUS", nFeat)); + if(JsonGetInt(GffGetByte(jCreature, "BaseAttackBonus")) < n2DAStat) return FALSE; + n2DAStat = StringToInt(Get2DAString("feat", "MINSTR", nFeat)); + if(JsonGetInt(GffGetByte(jCreature, "Str")) < n2DAStat) return FALSE; + n2DAStat = StringToInt(Get2DAString("feat", "MINDEX", nFeat)); + if(JsonGetInt(GffGetByte(jCreature, "Dex")) < n2DAStat) return FALSE; + n2DAStat = StringToInt(Get2DAString("feat", "MINCON", nFeat)); + if(JsonGetInt(GffGetByte(jCreature, "Con")) < n2DAStat) return FALSE; + n2DAStat = StringToInt(Get2DAString("feat", "MININT", nFeat)); + if(JsonGetInt(GffGetByte(jCreature, "Int")) < n2DAStat) return FALSE; + n2DAStat = StringToInt(Get2DAString("feat", "MINWIS", nFeat)); + if(JsonGetInt(GffGetByte(jCreature, "Wis")) < n2DAStat) return FALSE; + n2DAStat = StringToInt(Get2DAString("feat", "MINCHA", nFeat)); + if(JsonGetInt(GffGetByte(jCreature, "Cha")) < n2DAStat) return FALSE; + 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 != "") + { + nSpellLevel = StringToInt(Get2DAString(s2DAName, "NumSpellLevels", nLevel - 1)) - 1; + } + if(nSpellLevel < n2DAStat) return FALSE; + n2DAStat = StringToInt(Get2DAString("feat", "PREREQFEAT1", nFeat)); + if(n2DAStat > 0) + { + // ************************************** 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; + } + 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; + } + string s2DAStat = Get2DAString("feat", "REQSKILL", nFeat); + if(s2DAStat != "") + { + n2DAStat = StringToInt(s2DAStat); + int bCanUse; + if(Get2DAString("skills", "AllClassesCanUse", n2DAStat) == "1") bCanUse = TRUE; + else + { + string sClsSkill2DA = Get2DAString("classes", "SkillsTable", nClass); + int bPass, nClassSkill, nRow, nMaxRow = Get2DARowCount(sClsSkill2DA); + while(nRow < nMaxRow) + { + nClassSkill = StringToInt(Get2DAString(sClsSkill2DA, "SkillIndex", nRow)); + if(nClassSkill == n2DAStat) + { + bCanUse = TRUE; + break; + } + nRow++; + } + } + if(bCanUse) + { + int nSkillReq = StringToInt(Get2DAString("feat", "ReqSkillMinRanks", n2DAStat)); + // ************************** Add code to check jCreatures skills. + if(GetSkillRank(n2DAStat, oCreature, TRUE) < nSkillReq) return FALSE; + } + else return FALSE; + } + s2DAStat = Get2DAString("feat", "REQSKILL2", nFeat); + if(s2DAStat != "") + { + n2DAStat = StringToInt(s2DAStat); + int bCanUse; + if(Get2DAString("skills", "AllClassesCanUse", n2DAStat) == "1") bCanUse = TRUE; + else + { + string sClsSkill2DA = Get2DAString("classes", "SkillsTable", nClass); + int bPass, nClassSkill, nRow, nMaxRow = Get2DARowCount(sClsSkill2DA); + while(nRow < nMaxRow) + { + nClassSkill = StringToInt(Get2DAString(sClsSkill2DA, "SkillIndex", nRow)); + if(nClassSkill == n2DAStat) + { + bCanUse = TRUE; + break; + } + nRow++; + } + } + if(bCanUse) + { + int nSkillReq = StringToInt(Get2DAString("feat", "ReqSkillMinRanks2", n2DAStat)); + if(GetSkillRank(n2DAStat, oCreature, TRUE) < nSkillReq) return FALSE; + } + else return FALSE; + } + n2DAStat = StringToInt(Get2DAString("feat", "MinLevel", nFeat)); + if(n2DAStat > 0) + { + int bPass, nClassPosition, nPositionClass, nPositionLevel; + int nClassRequired = StringToInt(Get2DAString("feat", "MinLevelClass", nFeat)); + while(nClassPosition < AI_MAX_CLASSES_PER_CHARACTER) + { + // ***************************** Rework to check jCreature class list instead. + nPositionClass = GetClassByPosition(nClassPosition, oCreature); + if(nPositionClass == nClassRequired) + { + nPositionLevel = GetLevelByPosition(nClassPosition, oCreature); + if(nPositionLevel < n2DAStat) return FALSE; + else bPass = TRUE; + } + nClassPosition++; + } + if(!bPass) return FALSE; + } + n2DAStat = StringToInt(Get2DAString("feat", "MinFortSave", nFeat)); + if(JsonGetInt(GffGetChar(jCreature, "FortSaveThrow")) < n2DAStat) return FALSE; + s2DAStat = Get2DAString("feat", "PreReqEpic", nFeat); + if(s2DAStat == "1") return FALSE; + return TRUE; +} +json ResetFeats(json jHenchman, object oHenchman) +{ + int nLevel = 0; + // We remake the Feat list if the character doesn't have a level list! + json jFeatList = JsonArray(); + json jFeat; + int nRace = GetRacialType(oHenchman); + string sRace2DAName = Get2DAString("racialtypes", "FeatsTable", nRace); + // Give racial feats. + WriteTimestampedLogEntry("pinc_henchmen, 972, Checking for racial feats."); + int nRaceRow, nRaceFeat; + int nRaceMaxRow = Get2DARowCount(sRace2DAName); + while(nRaceRow < nRaceMaxRow) + { + nRaceFeat = StringToInt(Get2DAString(sRace2DAName, "FeatIndex", nRaceRow)); + jFeat = JsonObject(); + jFeat = GffAddWord(jFeat, "Feat", nRaceFeat); + jFeat = JsonObjectSet(jFeat, "__struct_id", JsonInt(1)); + jFeatList = JsonArrayInsert(jFeatList, jFeat); + WriteTimestampedLogEntry("pinc_henchmen, 982, Adding racial feat: " + + Get2DAString("feat", "LABEL", nRaceFeat)); + nRaceRow++; + } + // Give class feats. + WriteTimestampedLogEntry("pinc_henchmen, 972, Checking for class feats."); + int nClass = GetClassByPosition(1, oHenchman); + string sGranted, sList; + string sClsFeat2DAName = Get2DAString("classes", "FeatsTable", nClass); + int nClassRow, nClassFeat, nClassMaxRow = Get2DARowCount(sClsFeat2DAName); + while(nClassRow < nClassMaxRow) + { + sGranted = Get2DAString(sClsFeat2DAName, "GrantedOnLevel", nClassRow); + if(sGranted == "1") + { + sList = Get2DAString(sClsFeat2DAName, "List", nClassRow); + if(sList == "3") + { + nClassFeat = StringToInt(Get2DAString(sClsFeat2DAName, "FeatIndex", nClassRow)); + jFeat = JsonObject(); + jFeat = GffAddWord(jFeat, "Feat", nClassFeat); + jFeat = JsonObjectSet(jFeat, "__struct_id", JsonInt(1)); + jFeatList = JsonArrayInsert(jFeatList, jFeat); + WriteTimestampedLogEntry("pinc_henchmen, 1005, Adding class feat: " + + Get2DAString("feat", "LABEL", nClassFeat)); + } + } + nClassRow++; + } + // Give any bonus feats from package. + WriteTimestampedLogEntry("pinc_henchmen, 1012, Checking for selectable feats."); + int nPackageFeat, nPackageRow; + string sBonusFeat2DAName = Get2DAString("classes", "BonusFeatsTable", nClass); + int nNumOfFeats = StringToInt(Get2DAString(sBonusFeat2DAName, "Bonus", nLevel)); + string sPackage2DAName = Get2DAString("packages", "FeatPref2DA", nClass); + int nPackageMaxRow = Get2DARowCount(sPackage2DAName); + // Give bonus feats based on the package. + nPackageRow = 0; + if(nNumOfFeats > 0) + { + while(nPackageRow < nPackageMaxRow) + { + nPackageFeat = StringToInt(Get2DAString(sPackage2DAName, "FeatIndex", nPackageRow)); + nClassRow = 0; + while(nClassRow < nClassMaxRow) + { + nClassFeat = StringToInt(Get2DAString(sClsFeat2DAName, "FeatIndex", nClassRow)); + if(nClassFeat == nPackageFeat) + { + sList = Get2DAString(sClsFeat2DAName, "List", nClassRow); + if((sList == "1" || sList == "2") && CanSelectFeat(jHenchman, oHenchman, nClassFeat)) + { + 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: " + + Get2DAString("feat", "LABEL", nPackageFeat)); + nNumOfFeats--; + } + } + nClassRow++; + } + if(nNumOfFeats < 1) break; + nPackageRow++; + } + } + // Give picked feats from package. + WriteTimestampedLogEntry("pinc_henchmen, 972, Checking for select feats."); + nNumOfFeats = 1; + if(GetHasFeat(FEAT_QUICK_TO_MASTER, oHenchman)) nNumOfFeats++; + nPackageRow = 0; + while(nPackageRow < nPackageMaxRow || nNumOfFeats > 0) + { + nClassRow = 0; + nPackageFeat = StringToInt(Get2DAString(sPackage2DAName, "FeatIndex", nPackageRow)); + if(CanSelectFeat(jHenchman, oHenchman, nPackageFeat)) + { + 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: " + + Get2DAString("feat", "LABEL", nPackageFeat)); + nNumOfFeats--; + } + if(nNumOfFeats < 1) break; + nPackageRow++; + } + WriteTimestampedLogEntry("pinc_henchmen, 1071, Adding feat list."); + jHenchman = GffReplaceList(jHenchman, "FeatList", jFeatList); + return jHenchman; +} +json ResetSkills(json jHenchman, object oHenchman) +{ + // 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; + json jSkillList = JsonArray(); + json jSkill; + // Setup the Skill List. + WriteTimestampedLogEntry("pinc_henchmen, 1087, Generating skill list."); + int nIndex, nSkillMaxRow = Get2DARowCount("skills"); + for(nIndex = 0; nIndex < nSkillMaxRow; nIndex++) + { + jSkill = JsonObject(); + jSkill = GffAddByte(jSkill, "Rank", 0); + jSkill = JsonObjectSet(jSkill, "__struct_id", JsonInt(0)); + jSkillList = JsonArrayInsert(jSkillList, jSkill); + } + // Give skill points based on the package. + WriteTimestampedLogEntry("pinc_henchmen, 1097, Gets " + IntToString(nSkillPoints) + " skill points."); + int nPackageSkill, nPackageRow, nCurrentRanks, bCrossClass, nClassRow, nNewRanks; + string sPackage2DAName = Get2DAString("packages", "SkillPref2DA", nClass); + int nPackageMaxRow = Get2DARowCount(sPackage2DAName); + string sClass2DAName = Get2DAString("classes", "SkillsTable", nClass); + int nClassMaxRow = Get2DARowCount(sClass2DAName); + nPackageRow = 0; + while(nPackageRow < nPackageMaxRow && nSkillPoints > 0) + { + nPackageSkill = StringToInt(Get2DAString(sPackage2DAName, "SkillIndex", nPackageRow)); + jSkill = JsonArrayGet(jSkillList, nPackageSkill); + nCurrentRanks = JsonGetInt(GffGetByte(jSkill, "Rank")); + nClassRow = 0; + while(nClassRow < nClassMaxRow) + { + if(nPackageSkill == StringToInt(Get2DAString(sClass2DAName, "SkillIndex", nClassRow))) + { + bCrossClass = Get2DAString(sClass2DAName, "ClassSkill", nClassRow) == "0"; + break; + } + nClassRow++; + } + if(bCrossClass) nNewRanks = (nMaxRanks / 2) - nCurrentRanks; + else nNewRanks = nMaxRanks - nCurrentRanks; + if(nNewRanks > nSkillPoints) nNewRanks = nSkillPoints; + if(nNewRanks > 0) + { + jSkill = GffReplaceByte(jSkill, "Rank", nCurrentRanks + nNewRanks); + jSkillList = JsonArraySet(jSkillList, nPackageSkill, jSkill); + WriteTimestampedLogEntry("pinc_henchmen, 1126, Adding " + IntToString(nNewRanks) + + " ranks to " + Get2DAString("skills", "Label", nPackageSkill) + + " CrossClass: " + IntToString(bCrossClass)); + nSkillPoints -= nNewRanks; + } + nPackageRow++; + } + jHenchman = GffReplaceList(jHenchman, "SkillList", jSkillList); + return jHenchman; +} +json ResetSpellsKnown(json jClass, object oHenchman) +{ + WriteTimestampedLogEntry("pinc_henchmen, 1138, Checking for spells known."); + int nClass = GetClassByPosition(1, oHenchman); + WriteTimestampedLogEntry("pinc_henchmen, 1140, 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! + json jKnownList, jMemorizedList; + json jSpell, jSpellsPerDayList; + int bMemorizesSpells = StringToInt(Get2DAString("classes", "MemorizesSpells", nClass)); + int bSpellBookRestricted = StringToInt(Get2DAString("classes", "SpellBookRestricted", nClass)); + string sSpellKnown2DAName = Get2DAString("classes", "SpellKnownTable", nClass); + string sSpellGained2DAName = Get2DAString("classes", "SpellGainTable", nClass); + string sSpellTableColumn = Get2DAString("classes", "SpellTableColumn", nClass); + string sSpellPackage2DAName = Get2DAString("packages", "SpellPref2DA", nClass); + int nPackageSpell, nPackageRow; + int nPackageMaxRow = Get2DARowCount(sSpellPackage2DAName); + int nKnownSpellIndex, nSpellsKnown, nAbility, nSpellLevel = 0; + string sKnownListName, sSpellLevel, sPackageSpellLevel, sAbility; + // Cycle through all spell levels and reset. + while(nSpellLevel < 10) + { + sSpellLevel = IntToString(nSpellLevel); + WriteTimestampedLogEntry("pinc_henchmen, 1143, Checking Spell Level: " + sSpellLevel); + // Recreate the 0th and 1st level based on the package. + if(nSpellLevel < 2 && bSpellBookRestricted) + { + // Spellbook restricted that don't have a SpellsKnown2DAName + // get to keep all 0th level spells so we skip them. Example:Wizard + if(nSpellLevel != 0 || sSpellKnown2DAName != "") + { + // Classes that are spell book restricted but don't have a SpellKnownTable + // get 3 spells + Ability Modifier worth of spells like a wizard. + if(sSpellKnown2DAName == "") + { + sAbility = Get2DAString("classes", "SpellCastingAbil", nClass); + if(sAbility == "INT") nAbility = ABILITY_INTELLIGENCE; + else if(sAbility == "WIS") nAbility = ABILITY_WISDOM; + else if(sAbility == "CHA") nAbility = ABILITY_CHARISMA; + nSpellsKnown = 3 + GetAbilityModifier(nAbility, oHenchman); + } + else + { + nSpellsKnown = StringToInt(Get2DAString(sSpellKnown2DAName, "SpellLevel" + sSpellLevel, nLevel)); + } + WriteTimestampedLogEntry("pinc_henchmen, 1165, nSpellsKnown: " + IntToString(nSpellsKnown)); + jKnownList = JsonArray(); + nPackageRow = 0; + while(nPackageRow < nPackageMaxRow && nSpellsKnown > 0) + { + nPackageSpell = StringToInt(Get2DAString(sSpellPackage2DAName, "SpellIndex", nPackageRow)); + sPackageSpellLevel = Get2DAString("spells", sSpellTableColumn, nPackageSpell); + if(sPackageSpellLevel == sSpellLevel) + { + jSpell = JsonObject(); + jSpell = GffAddWord(jSpell, "Spell", nPackageSpell); + jSpell = JsonObjectSet(jSpell, "__struct_id", JsonInt(3)); + jKnownList = JsonArrayInsert(jKnownList, jSpell); + WriteTimestampedLogEntry("pinc_henchmen, 1178, Adding known spell: " + + Get2DAString("spells", "LABEL", nPackageSpell)); + nSpellsKnown--; + } + nPackageRow++; + } + if(JsonGetLength(jKnownList) == 0) + { + jClass = GffRemoveList(jClass, "KnownList" + sSpellLevel); + WriteTimestampedLogEntry("pinc_henchmen, 1187, Removing KnownList" + sSpellLevel); + } + else if(JsonGetType(GffGetList(jClass, "KnownList" + sSpellLevel)) != JSON_TYPE_NULL) + { + jClass = GffReplaceList(jClass, "KnownList" + sSpellLevel, jKnownList); + } + else jClass = GffAddList(jClass, "KnownList" + sSpellLevel, jKnownList); + } + } + // Remove all other known spell levels and memorized levels. + else + { + jKnownList = GffGetList(jClass, "KnownList" + sSpellLevel); + if(JsonGetType(jKnownList) != JSON_TYPE_NULL) + { + jClass = GffRemoveList(jClass, "KnownList" + sSpellLevel); + WriteTimestampedLogEntry("pinc_henchmen, 1203, Removing KnownList" + sSpellLevel); + } + } + if(bMemorizesSpells) + { + jMemorizedList = GffGetList(jClass, "MemorizedList" + sSpellLevel); + if(JsonGetType(jMemorizedList) != JSON_TYPE_NULL) + { + jClass = GffRemoveList(jClass, "MemorizedList" + sSpellLevel); + WriteTimestampedLogEntry("pinc_henchmen, 1210, Removing MemorizedList" + sSpellLevel); + } + } + else + { + jSpellsPerDayList = GffGetList(jClass, "SpellsPerDayList"); + nSpellsKnown = StringToInt(Get2DAString(sSpellGained2DAName, "SpellLevel"+ sSpellLevel, nLevel)); + jSpell = JsonArrayGet(jSpellsPerDayList, nSpellLevel); + jSpell = GffReplaceByte(jSpell, "NumSpellsLeft", nSpellsKnown); + jSpellsPerDayList = JsonArraySet(jSpellsPerDayList, nSpellLevel, jSpell); + jClass = GffReplaceList(jClass, "SpellsPerDayList", jSpellsPerDayList); + WriteTimestampedLogEntry("pinc_henchmen, 1223, Setting SpellsPerDay to " + + IntToString(nSpellsKnown)); + } + nSpellLevel++; + } + return jClass; +} +object ResetCharacter(object oPC, object oHenchman) +{ + SetLocalInt(oPC, "AI_IGNORE_NO_ASSOCIATE", TRUE); + RemoveHenchman(oPC, oHenchman); + 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")); + jClass = GffReplaceShort(jClass, "ClassLevel", 1); + // Delete extra classes. + int nClassIndex = JsonGetLength(jClassList) - 1; + while(nClassIndex > 0) + { + 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); + string s2DA = Get2DAString("classes", "AttackBonusTable", nClass); + int nAtk = StringToInt(Get2DAString(s2DA, "BAB", 0)); + jHenchman = GffReplaceByte(jHenchman, "BaseAttackBonus", nAtk); + s2DA = Get2DAString("classes", "SavingThrowTable", nClass); + int nSave = StringToInt(Get2DAString(s2DA, "FortSave", 0)); + jHenchman = GffReplaceChar(jHenchman, "FortSaveThrow", nSave); + nSave = StringToInt(Get2DAString(s2DA, "RefSave", 0)); + jHenchman = GffReplaceChar(jHenchman, "RefSaveThrow", nSave); + nSave = StringToInt(Get2DAString(s2DA, "WillSave", 0)); + jHenchman = GffReplaceChar(jHenchman, "WillSaveThrow", nSave); + json jLvlStatList = GffGetList(jHenchman, "LvlStatList"); + if(JsonGetType(jLvlStatList) != JSON_TYPE_NULL) + { + WriteTimestampedLogEntry("pinc_henchmen 1275, jLvlStatList: " + JsonDump(jLvlStatList, 4)); + int nLevel = 1, nLevelTrack = 1; + int nAbilityStatIncrease, nAbility; + string sAbility; + json jAbility; + json jLevel = JsonArrayGet(jLvlStatList, nLevel); + while(JsonGetType(jLevel) != JSON_TYPE_NULL) + { + WriteTimestampedLogEntry("inc_henchmen, 1297, 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) + { + nAbilityStatIncrease = JsonGetInt(jAbility); + if(nAbilityStatIncrease == ABILITY_STRENGTH) sAbility = "Str"; + if(nAbilityStatIncrease == ABILITY_DEXTERITY) sAbility = "Dex"; + if(nAbilityStatIncrease == ABILITY_CONSTITUTION) sAbility = "Con"; + if(nAbilityStatIncrease == ABILITY_INTELLIGENCE) sAbility = "Int"; + if(nAbilityStatIncrease == ABILITY_WISDOM) sAbility = "Wis"; + if(nAbilityStatIncrease == ABILITY_CHARISMA) sAbility = "Cha"; + nAbility = JsonGetInt(GffGetByte(jHenchman, sAbility)) - 1; + jHenchman = GffReplaceByte(jHenchman, sAbility, nAbility); + WriteTimestampedLogEntry("pinc_henchmen, 1314, Removing " + sAbility + " level bonus ability score point."); + } + jLvlStatList = JsonArrayDel(jLvlStatList, nLevel); + // Note: nLevel is not incremented since we are removing the previous level. + // there for when we get the same level again its the next level! + jLevel = JsonArrayGet(jLvlStatList, nLevel); + //SendMessageToPC(oPC, "jLvlStatList: " + JsonDump(jLvlStatList, 4)); + nLevelTrack++; + } + jHenchman = GffRemoveList(jHenchman, "LvlStatList"); + } + jHenchman = CreateLevelStatList(jHenchman, oHenchman, oPC, 1); + jHenchman = ResetSkills(jHenchman, oHenchman); + 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)); + location lLocation = GetLocation(oHenchman); + int nFamiliar, nCompanion; + object oCompanion = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oHenchman); + if(oCompanion != OBJECT_INVALID) nFamiliar = TRUE; + oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oHenchman); + if(oCompanion != OBJECT_INVALID) nCompanion = TRUE; + AssignCommand(oHenchman, SetIsDestroyable(TRUE, FALSE, FALSE)); + DestroyObject(oHenchman); + oHenchman = ai_AddHenchman(oPC, jHenchman, lLocation, nFamiliar, nCompanion); + return oHenchman; +} +// ********* New Henchman windows ********** +void CreateCharacterEditGUIPanel(object oPC, object oHenchman) +{ + // Set window to not save until it has been created. + SetLocalInt(oPC, "0_No_Win_Save", TRUE); + DelayCommand(0.5f, DeleteLocalInt (oPC, "0_No_Win_Save")); + // 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 = 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 = JsonArrayInsert(jGroupRow, NuiSpacer()); + // Add the group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 1 Row 2 *********************************************************** 350 / 259 + jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jGroupRow = CreateImage(jGroupRow, "", "port_resref", NUI_ASPECT_EXACTSCALED, NUI_HALIGN_CENTER, NUI_VALIGN_TOP, 140.0f, 160.0f); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + // Add the group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 1 Row 3 *********************************************************** 350 / 292 + jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jGroupRow = CreateButton (jGroupRow, "<", "btn_portrait_prev", 42.0f, 25.0f); + jGroupRow = CreateButton (jGroupRow, "Set", "btn_portrait_ok", 44.0f, 25.0f); + jGroupRow = CreateButton (jGroupRow, ">", "btn_portrait_next", 42.0f, 25.0f); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 1 Row 4 *********************************************************** 350 / 91 + jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jGroupRow = CreateLabel(jGroupRow, "Sound Set", "lbl_sound_set", 140.0, 10.0f, NUI_HALIGN_CENTER, NUI_VALIGN_BOTTOM); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + // Add the group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 1 Row 5 *********************************************************** 350 / 325 + jGroupRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jGroupRow = CreateCombo(jGroupRow, ArrayInsertSoundSets(oHenchman), "cmb_soundset", 140.0, 25.0); + jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + json jRow = JsonArrayInsert(JsonArray(), NuiGroup(NuiCol(jGroupCol))); + // Group 2 (Stats)********************************************************** 151 / 73 + // Group 2 Row 1 *********************************************************** 350 / 91 + jGroupRow = CreateLabel(JsonArray(), "", "lbl_stats", 150.0, 15.0, 0, NUI_VALIGN_BOTTOM, 0.0); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(JsonArray(), NuiRow(jGroupRow)); + + // Group 2 Row 2 *********************************************************** 350 / 243 + //json jAlign = CreateOptionsAlignment(oHenchman, 0); + //jGroupRow = CreateOptions(JsonArray(), "opt_lawchaos", NUI_DIRECTION_HORIZONTAL, jAlign, 60.0, 35.0); + // Add group row to the group column. + //jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 2 Row 3 *********************************************************** 350 / 243 + //jAlign = CreateOptionsAlignment(oHenchman, 1); + //jGroupRow = CreateOptions(JsonArray(), "opt_goodevil", NUI_DIRECTION_HORIZONTAL, jAlign, 60.0, 35.0); + //jGroupRow = JsonArrayInsert(jGroupRow, NuiSpacer()); + // Add group row to the group column. + //jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 2 Row 2 *********************************************************** 350 / 243 + json jClasses = CreateOptionsClasses(oHenchman); + jGroupRow = CreateOptions(JsonArray(), "opt_classes", NUI_DIRECTION_VERTICAL, jClasses, 150.0, 144.0); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 2 Row 3 *********************************************************** 350 / 276 + jGroupRow = CreateButton(JsonArray(), "Level Up", "btn_level_up", 150.0f, 25.0f, -1.0, "btn_level_up_tooltip"); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 2 Row 4 *********************************************************** 350 / 309 + jGroupRow = CreateButton (JsonArray(), "Reset Character", "btn_reset", 150.0f, 25.0f, -1.0, "btn_reset_tooltip"); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 2 Row 5 *********************************************************** 350 / 342 + jGroupRow = CreateCombo(JsonArray(), jArrayInsertClasses(), "cmb_class", 150.0, 25.0); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + // Group 2 Row 6 *********************************************************** 350 / 375 + int nClassOption = GetLocalInt(oHenchman, "CLASS_OPTION_POSITION"); + int nClass = GetClassByPosition(nClassOption + 1, oHenchman); + int bNoClass = FALSE; + if(nClass == CLASS_TYPE_INVALID) + { + nClass = GetLocalInt(oHenchman, "CLASS_SELECTED_" + IntToString(nClassOption + 1)); + bNoClass = TRUE; + } + string sClass = IntToString(nClass); + jGroupRow = CreateCombo(JsonArray(), ArrayInsertPackages(sClass), "cmb_package", 150.0, 25.0); + // Add group row to the group column. + jGroupCol = JsonArrayInsert(jGroupCol, NuiRow(jGroupRow)); + jRow = JsonArrayInsert(jRow, NuiGroup(NuiCol(jGroupCol))); + // Add the row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 5 (text edit box)**************************************************** 350 / 518 + jRow = CreateTextEditBox(JsonArray(), "desc_placeholder", "desc_value", 1000, TRUE, 350.0, 150.0, "desc_tooltip"); + // Add the row to the column. + jCol = JsonArrayInsert(jCol, NuiRow (jRow)); + // Row 6 (button)*********************************************************** 350/ 546 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton (jRow, "Save Description", "btn_desc_save", 150.0f, 20.0f); + jRow = JsonArrayInsert(jRow, NuiSpacer()); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow (jRow)); + // Set the Layout of the window. + json jLayout = NuiCol (jCol); + // Get the window location to restore it from the database. + CheckHenchmanDataAndInitialize(oPC, "0"); + json jData = GetHenchmanDbJson(oPC, "henchman", "0"); + json jGeometry = JsonObjectGet(jData, "henchman_edit_nui"); + float fX = JsonGetFloat(JsonObjectGet(jGeometry, "x")); + float fY = JsonGetFloat(JsonObjectGet(jGeometry, "y")); + if(fX == 0.0 && fY == 0.0) + { + fX = -1.0; + fY = -1.0; + } + string sName = GetName(oHenchman); + if(GetStringRight(sName, 1) == "s") sName = sName + "'"; + else sName = sName + "'s"; + int nToken = SetWindow (oPC, jLayout, "henchman_edit_nui", sName + " Character editor", + fX, fY, 380.0, 588.0, FALSE, FALSE, TRUE, FALSE, TRUE, "pe_henchmen"); + // Set all binds, events, and watches. + int nID = GetPortraitId (oPC); + NuiSetUserData(oPC, nToken, JsonInt(nID)); + string sResRef = GetPortraitResRef(oHenchman); + NuiSetBindWatch(oPC, nToken, "window_geometry", TRUE); + NuiSetBind(oPC, nToken, "char_name", JsonString(GetName(oHenchman))); + NuiSetBindWatch(oPC, nToken, "char_name", TRUE); + NuiSetBind(oPC, nToken, "char_name_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "port_name", JsonString(sResRef)); + NuiSetBindWatch(oPC, nToken, "port_name", TRUE); + NuiSetBind(oPC, nToken, "port_name_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "port_resref_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "port_resref_image", JsonString(sResRef + "l")); + NuiSetBind(oPC, nToken, "port_tooltip", JsonString (" You may also type the portrait file name.")); + // Set buttons active. + NuiSetBind(oPC, nToken, "btn_portrait_prev_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_portrait_next_event", JsonBool(TRUE)); + int nSelection = GetSelectionBySoundSet2DA(oHenchman, GetSoundset(oHenchman)); + NuiSetBind(oPC, nToken, "cmb_soundset_selected", JsonInt(nSelection)); + NuiSetBindWatch(oPC, nToken, "cmb_soundset_selected", TRUE); + NuiSetBind(oPC, nToken, "cmb_soundset_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_desc_save_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_portrait_ok_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "desc_tooltip", JsonString(" You can use color codes!")); + string sDescription = GetDescription(oHenchman); + NuiSetBind(oPC, nToken, "desc_value_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "desc_value", JsonString (sDescription)); + // Setup the henchman window. + string sStats = GetAlignText(oHenchman) + " "; + if(GetGender(oHenchman) == GENDER_MALE) sStats += "Male "; + else sStats += "Female "; + sStats += GetStringByStrRef (StringToInt (Get2DAString ("racialtypes", "Name", GetRacialType (oHenchman)))); + NuiSetBind(oPC, nToken, "lbl_stats_label", JsonString(sStats)); + json jHenchman = ObjectToJson(oHenchman); + NuiSetBind(oPC, nToken, "opt_classes_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "opt_classes_value", JsonInt(nClassOption)); + NuiSetBind(oPC, nToken, "btn_level_up_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_level_up_tooltip", JsonString(" Levels the character up by one level in selected class.")); + if(ai_GetIsCharacter(oHenchman)) NuiSetBind(oPC, nToken, "btn_reset_event", JsonBool(FALSE)); + else NuiSetBind(oPC, nToken, "btn_reset_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "btn_reset_tooltip", JsonString(" Resets the character to level 1.")); + nSelection = GetSelectionByClass2DA(nClass); + NuiSetBind(oPC, nToken, "cmb_class_selected", JsonInt(nSelection)); + NuiSetBindWatch(oPC, nToken, "cmb_class_selected", bNoClass); + NuiSetBind(oPC, nToken, "cmb_class_event", JsonBool(bNoClass)); + int nPackage = GetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nClassOption + 1)); + if(nPackage == 0) + { + nPackage = GetPackageBySelection2DA(sClass, 0); + SetLocalInt(oHenchman, "PACKAGE_SELECTED_" + IntToString(nClassOption + 1), nPackage); + } + 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)); +} +void CreateCharacterDescriptionNUI(object oPC, string sName, string sIcon, string sDescription) +{ + // Row 1 ******************************************************************* 500 / 469 + json jRow = CreateImage(JsonArray(), "", "char_icon", NUI_ASPECT_FIT, NUI_HALIGN_CENTER, NUI_VALIGN_MIDDLE, 40.0, 40.0); + jRow = CreateTextBox(jRow, "char_text", 380.0, 400.0); + // Add row to the column. + json jCol = JsonArrayInsert(JsonArray(), NuiRow(jRow)); + // Row 2 ******************************************************************* 500 / 522 + jRow = JsonArrayInsert(JsonArray(), NuiSpacer()); + jRow = CreateButton(jRow, "OK", "btn_ok", 150.0f, 45.0f); + // Add row to the column. + jCol = JsonArrayInsert(jCol, NuiRow(jRow)); + // Set the Layout of the window. + json jLayout = NuiCol(jCol); + int nToken = SetWindow(oPC, jLayout, "char_description_nui", sName, + -1.0, -1.0, 460.0f, 537.0 + 12.0f, FALSE, FALSE, TRUE, FALSE, TRUE, "pe_henchmen"); + json jData = JsonArrayInsert(JsonArray(), JsonString(ObjectToString(oPC))); + NuiSetUserData(oPC, nToken, jData); + // Row 1 + NuiSetBind(oPC, nToken, "char_icon_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "char_icon_image", JsonString(sIcon)); + NuiSetBind(oPC, nToken, "char_text_event", JsonBool(TRUE)); + NuiSetBind(oPC, nToken, "char_text", JsonString(sDescription)); + // Row 2 + NuiSetBind(oPC, nToken, "btn_ok_event", JsonBool(TRUE)); +} + diff --git a/_module/nss/x0_ch_hen_block.nss b/_module/nss/x0_ch_hen_block.nss new file mode 100644 index 00000000..d7d86a35 --- /dev/null +++ b/_module/nss/x0_ch_hen_block.nss @@ -0,0 +1,14 @@ +//::////////////////////////////////////////////////// +//:: X0_CH_HEN_BLOCK +/* + OnBlocked handler for henchmen/associates. + */ +//::////////////////////////////////////////////////// +//:: Copyright (c) 2002 Floodgate Entertainment +//:: Created By: Naomi Novik +//:: Created On: 01/06/2003 +//::////////////////////////////////////////////////// +void main() +{ + ExecuteScript("nw_ch_ace"); +} diff --git a/_module/nss/x0_ch_hen_combat.nss b/_module/nss/x0_ch_hen_combat.nss new file mode 100644 index 00000000..d3e359b0 --- /dev/null +++ b/_module/nss/x0_ch_hen_combat.nss @@ -0,0 +1,36 @@ +//:://///////////////////////////////////////////// +//:: Associate: End of Combat End +//:: NW_CH_AC3 +//:: Copyright (c) 2001 Bioware Corp. +//::////////////////////////////////////////////// +/* + Calls the end of combat script every round +*/ +//::////////////////////////////////////////////// +//:: Created By: Preston Watamaniuk +//:: Created On: Oct 16, 2001 +//::////////////////////////////////////////////// +//::////////////////////////////////////////////// +//:: Modified By: Deva Winblood +//:: Modified On: Jan 16th, 2008 +//:: Added Support for Mounted Combat Feat Support +//::////////////////////////////////////////////// +#include "x0_inc_henai" + +void main() +{ + if (!GetLocalInt(GetModule(),"X3_NO_MOUNTED_COMBAT_FEAT")) + { // set variables on target for mounted combat + DeleteLocalInt(OBJECT_SELF,"bX3_LAST_ATTACK_PHYSICAL"); + DeleteLocalInt(OBJECT_SELF,"nX3_HP_BEFORE"); + DeleteLocalInt(OBJECT_SELF,"bX3_ALREADY_MOUNTED_COMBAT"); + } // set variables on target for mounted combat + if(!GetSpawnInCondition(NW_FLAG_SET_WARNINGS)) + { + ExecuteScript("nw_ch_ac3"); + } + if(GetSpawnInCondition(NW_FLAG_END_COMBAT_ROUND_EVENT)) + { + SignalEvent(OBJECT_SELF, EventUserDefined(1003)); + } +} diff --git a/_module/nss/x0_ch_hen_conv.nss b/_module/nss/x0_ch_hen_conv.nss new file mode 100644 index 00000000..6943250d --- /dev/null +++ b/_module/nss/x0_ch_hen_conv.nss @@ -0,0 +1,91 @@ +//::////////////////////////////////////////////////// +//:: X0_CH_HEN_CONV +// OnDialogue event handler for henchmen/associates. +//::////////////////////////////////////////////////// +//:: Copyright (c) 2002 Floodgate Entertainment +//:: Created By: Naomi Novik +//:: Created On: 01/05/2003 +//::////////////////////////////////////////////////// +#include "x0_inc_henai" +#include "x0_i0_henchman" +#include "0i_associates" +//* GeorgZ - Put in a fix for henchmen talking even if they are petrified +int AbleToTalk(object oSelf) +{ + if (GetHasEffect(EFFECT_TYPE_CONFUSED, oSelf) || GetHasEffect(EFFECT_TYPE_DOMINATED, oSelf) || + GetHasEffect(EFFECT_TYPE_PETRIFY, oSelf) || GetHasEffect(EFFECT_TYPE_PARALYZE, oSelf) || + GetHasEffect(EFFECT_TYPE_STUNNED, oSelf) || GetHasEffect(EFFECT_TYPE_FRIGHTENED, oSelf) + ) + { + return FALSE; + } + + return TRUE; +} +void main() +{ + object oCreature = OBJECT_SELF; + // * XP2, special handling code for interjections + // * This script only fires if someone inits with me. + // * with that in mind, I am now clearing any interjections + // * that the character might have on themselves. + if (GetLocalInt(GetModule(), "X2_L_XP2") == TRUE) + { + SetLocalInt(oCreature, "X2_BANTER_TRY", 0); + SetHasInterjection(GetMaster(oCreature), FALSE); + SetLocalInt(oCreature, "X0_L_BUSY_SPEAKING_ONE_LINER", 0); + SetOneLiner(FALSE, 0); + } + object oLastSpeaker = GetLastSpeaker(); + if (GetTag(oCreature) == "x0_hen_dee") + { + string sCall = GetCampaignString("Deekin", "q6_Deekin_Call"+ GetName(oLastSpeaker), oLastSpeaker); + if (sCall == "") SetCustomToken(1001, GetStringByStrRef(40570)); + else SetCustomToken(1001, sCall); + } + // If we are disabled then we can't listen or talk, Busy is checked in ai_SelectAssociateCommand(). + if (GetIsHenchmanDying(oCreature) || ai_Disabled(oCreature)) return; + object oMaster = GetMaster(); + int nMatch = GetListenPatternNumber(); + object oIntruder; + if (nMatch == -1) + { + if (!ai_GetIsBusy(oCreature)) + { + ai_ClearCreatureActions(); + string sDialogFileToUse = GetDialogFileToUse(GetLastSpeaker()); + BeginConversation(sDialogFileToUse); + } + } + else + { + // listening pattern matched + if (GetIsObjectValid(oMaster)) ai_SelectAssociateCommand(oCreature, oLastSpeaker, nMatch); + // we don't have a master, behave in default way + else if (GetIsObjectValid(oLastSpeaker) && + !GetIsPC(oLastSpeaker) && + GetIsFriend(oLastSpeaker)) + { + object oIntruder = OBJECT_INVALID; + // Determine the intruder if any + if(nMatch == 4) oIntruder = GetLocalObject(oLastSpeaker, "NW_BLOCKER_INTRUDER"); + else if (nMatch == 5) + { + oIntruder = GetLastHostileActor(oLastSpeaker); + if(!GetIsObjectValid(oIntruder)) + { + oIntruder = GetAttemptedAttackTarget(); + if(!GetIsObjectValid(oIntruder)) oIntruder = GetAttemptedSpellTarget(); + } + } + // Actually respond to the shout + RespondToShout(oLastSpeaker, nMatch, oIntruder); + } + } + // Signal user-defined event + if(GetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT)) + { + SignalEvent(oCreature, EventUserDefined(EVENT_DIALOGUE)); + } +} + diff --git a/_module/nss/x0_ch_hen_rest.nss b/_module/nss/x0_ch_hen_rest.nss index 10739d1e..8d2fc789 100644 --- a/_module/nss/x0_ch_hen_rest.nss +++ b/_module/nss/x0_ch_hen_rest.nss @@ -8,59 +8,8 @@ //:: Created By: Naomi Novik //:: Created On: 01/06/2003 //::////////////////////////////////////////////////// -#include "inc_newspellbook" void main() { - object oSelf = OBJECT_SELF; - DelayCommand(30.5, ExecuteScript("_golemstats", OBJECT_SELF)); - // modifed by primogenitor - // aribeth uses her blackguard spellbook - if(GetTag(oSelf) == "H2_Aribeth") - { - //check spellbook has been setup - if(!GetLocalInt(oSelf, "PRC_Aribeth_Spellbook")) - { - persistant_array_create(oSelf, "Spellbook1_31"); - persistant_array_create(oSelf, "Spellbook2_31"); - persistant_array_create(oSelf, "Spellbook3_31"); - persistant_array_create(oSelf, "Spellbook4_31"); - persistant_array_set_int(oSelf, "Spellbook1_31", 0, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_MAGIC_WEAPON)); - persistant_array_set_int(oSelf, "Spellbook1_31", 1, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DOOM)); - persistant_array_set_int(oSelf, "Spellbook1_31", 2, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DOOM)); - persistant_array_set_int(oSelf, "Spellbook1_31", 3, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DOOM)); - persistant_array_set_int(oSelf, "Spellbook1_31", 4, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DOOM)); - persistant_array_set_int(oSelf, "Spellbook1_31", 5, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DOOM)); - persistant_array_set_int(oSelf, "Spellbook1_31", 6, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DOOM)); - persistant_array_set_int(oSelf, "Spellbook1_31", 7, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DOOM)); - - persistant_array_set_int(oSelf, "Spellbook2_31", 0, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_BULLS_STRENGTH)); - persistant_array_set_int(oSelf, "Spellbook2_31", 1, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DARKNESS)); - persistant_array_set_int(oSelf, "Spellbook2_31", 2, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_EAGLE_SPLEDOR)); - persistant_array_set_int(oSelf, "Spellbook2_31", 3, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_DARKNESS)); - persistant_array_set_int(oSelf, "Spellbook2_31", 4, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_MODERATE_WOUNDS)); - persistant_array_set_int(oSelf, "Spellbook2_31", 5, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_MODERATE_WOUNDS)); - persistant_array_set_int(oSelf, "Spellbook2_31", 6, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_MODERATE_WOUNDS)); - persistant_array_set_int(oSelf, "Spellbook2_31", 7, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_MODERATE_WOUNDS)); - - persistant_array_set_int(oSelf, "Spellbook3_31", 0, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_PROTECTION_FROM_ELEMENTS)); - persistant_array_set_int(oSelf, "Spellbook3_31", 1, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_PROTECTION_FROM_ELEMENTS)); - persistant_array_set_int(oSelf, "Spellbook3_31", 2, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_CONTAGION)); - persistant_array_set_int(oSelf, "Spellbook3_31", 3, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_CONTAGION)); - persistant_array_set_int(oSelf, "Spellbook3_31", 4, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_CONTAGION)); - persistant_array_set_int(oSelf, "Spellbook3_31", 5, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_CONTAGION)); - persistant_array_set_int(oSelf, "Spellbook3_31", 6, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_CONTAGION)); - persistant_array_set_int(oSelf, "Spellbook3_31", 7, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_CONTAGION)); - - persistant_array_set_int(oSelf, "Spellbook4_31", 0, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_SUMMON_CREATURE_IV)); - persistant_array_set_int(oSelf, "Spellbook4_31", 1, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_CRITICAL_WOUNDS)); - persistant_array_set_int(oSelf, "Spellbook4_31", 2, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_SUMMON_CREATURE_IV)); - persistant_array_set_int(oSelf, "Spellbook4_31", 3, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_CRITICAL_WOUNDS)); - persistant_array_set_int(oSelf, "Spellbook4_31", 4, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_CRITICAL_WOUNDS)); - persistant_array_set_int(oSelf, "Spellbook4_31", 5, RealSpellToSpellbookID(CLASS_TYPE_BLACKGUARD, SPELL_INFLICT_CRITICAL_WOUNDS)); - SetLocalInt(oSelf, "PRC_Aribeth_Spellbook", TRUE); - } - CheckNewSpellbooks(oSelf); - } - + ExecuteScript("nw_ch_aca"); }